| // Copyright (c) 2010 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 <string> |
| #include "base/file_path.h" |
| #include "base/waitable_event.h" |
| #include "chrome_frame/cfproxy_private.h" |
| #include "chrome/test/automation/automation_messages.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gmock_mutant.h" |
| |
| using testing::_; |
| using testing::DoAll; |
| using testing::NotNull; |
| using testing::Return; |
| using testing::StrictMock; |
| using testing::InvokeWithoutArgs; |
| using testing::WithoutArgs; |
| using testing::CreateFunctor; |
| using testing::StrEq; |
| using testing::Eq; |
| |
| // There is not much to test here since CFProxy is pretty dumb. |
| struct MockFactory : public ChromeProxyFactory { |
| MOCK_METHOD0(CreateProxy, ChromeProxy*()); |
| }; |
| |
| struct MockChromeProxyDelegate : public ChromeProxyDelegate { |
| MOCK_METHOD1(Connected, void(ChromeProxy* proxy)); |
| MOCK_METHOD2(PeerLost, void(ChromeProxy*, enum DisconnectReason reason)); |
| MOCK_METHOD0(Disconnected, void()); |
| MOCK_METHOD0(tab_handle, int()); |
| |
| MOCK_METHOD4(Completed_CreateTab, void(bool success, HWND chrome_wnd, |
| HWND tab_window, int tab_handle)); |
| MOCK_METHOD4(Completed_ConnectToTab, void(bool success, HWND chrome_window, |
| HWND tab_window, int tab_handle)); |
| MOCK_METHOD2(Completed_Navigate, void(bool success, |
| enum AutomationMsg_NavigationResponseValues res)); |
| MOCK_METHOD3(Completed_InstallExtension, void(bool success, |
| enum AutomationMsg_ExtensionResponseValues res, SyncMessageContext* ctx)); |
| MOCK_METHOD3(Completed_LoadExpandedExtension, void(bool success, |
| enum AutomationMsg_ExtensionResponseValues res, SyncMessageContext* ctx)); |
| MOCK_METHOD2(Completed_GetEnabledExtensions, void(bool success, |
| const std::vector<FilePath>* v)); |
| |
| // Network requests from Chrome. |
| MOCK_METHOD2(Network_Start, void(int request_id, |
| const IPC::AutomationURLRequest& request_info)); |
| MOCK_METHOD2(Network_Read, void(int request_id, int bytes_to_read)); |
| MOCK_METHOD2(Network_End, void(int request_id, const URLRequestStatus& s)); |
| MOCK_METHOD1(Network_DownloadInHost, void(int request_id)); |
| MOCK_METHOD2(GetCookies, void(const GURL& url, int cookie_id)); |
| MOCK_METHOD2(SetCookie, void(const GURL& url, const std::string& cookie)); |
| |
| // Navigation progress notifications. |
| MOCK_METHOD2(NavigationStateChanged, void(int flags, |
| const IPC::NavigationInfo& nav_info)); |
| MOCK_METHOD1(UpdateTargetUrl, void(const std::wstring& url)); |
| MOCK_METHOD2(NavigationFailed, void(int error_code, const GURL& gurl)); |
| MOCK_METHOD1(DidNavigate, void(const IPC::NavigationInfo& navigation_info)); |
| MOCK_METHOD1(TabLoaded, void(const GURL& url)); |
| |
| // |
| MOCK_METHOD3(OpenURL, void(const GURL& url_to_open, const GURL& referrer, |
| int open_disposition)); |
| MOCK_METHOD1(GoToHistoryOffset, void(int offset)); |
| MOCK_METHOD3(MessageToHost, void(const std::string& message, |
| const std::string& origin, const std::string& target)); |
| |
| // Misc. UI. |
| MOCK_METHOD1(HandleAccelerator, void(const MSG& accel_message)); |
| MOCK_METHOD3(HandleContextMenu, void(HANDLE menu_handle, int align_flags, |
| const IPC::ContextMenuParams& params)); |
| MOCK_METHOD1(TabbedOut, void(bool reverse)); |
| |
| // |
| MOCK_METHOD0(TabClosed, void()); |
| MOCK_METHOD1(AttachTab, |
| void(const IPC::AttachExternalTabParams& attach_params)); |
| }; |
| |
| struct MockSender : public IPC::Message::Sender { |
| MOCK_METHOD1(Send, bool(IPC::Message* m)); |
| }; |
| |
| struct MockCFProxyTraits : public CFProxyTraits { |
| MOCK_METHOD2(DoCreateChannel, IPC::Message::Sender*(const std::string& id, |
| IPC::Channel::Listener* l)); |
| MOCK_METHOD1(CloseChannel, void(IPC::Message::Sender* s)); |
| MOCK_METHOD1(LaunchApp, bool(const std::wstring& cmd_line)); |
| |
| // Forward the CreateChannel to DoCreateChannel, but save the ipc_thread |
| // and the listener (i.e. proxy implementation of Channel::Listener) |
| virtual IPC::Message::Sender* CreateChannel(const std::string& id, |
| IPC::Channel::Listener* l) { |
| ipc_loop = MessageLoop::current(); |
| listener = l; |
| return this->DoCreateChannel(id, l); |
| } |
| |
| // Simulate some activity in the IPC thread. |
| // You may find API_FIRE_XXXX macros (see below) handy instead. |
| void FireConnect(base::TimeDelta t) { |
| ASSERT_TRUE(ipc_loop != NULL); |
| ipc_loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(listener, |
| &IPC::Channel::Listener::OnChannelConnected, 0), t.InMilliseconds()); |
| } |
| |
| void FireError(base::TimeDelta t) { |
| ASSERT_TRUE(ipc_loop != NULL); |
| ipc_loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(listener, |
| &IPC::Channel::Listener::OnChannelError), t.InMilliseconds()); |
| } |
| |
| void FireMessage(const IPC::Message& m, base::TimeDelta t) { |
| ASSERT_TRUE(ipc_loop != NULL); |
| ipc_loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(listener, |
| &IPC::Channel::Listener::OnMessageReceived, m), t.InMilliseconds()); |
| } |
| |
| MockCFProxyTraits() : ipc_loop(NULL) {} |
| MockSender sender; |
| private: |
| MessageLoop* ipc_loop; |
| IPC::Channel::Listener* listener; |
| }; |
| |
| // Handy macros when we want so similate something on the IPC thread. |
| #define API_FIRE_CONNECT(api, t) InvokeWithoutArgs(CreateFunctor(&api, \ |
| &MockCFProxyTraits::FireConnect, t)) |
| #define API_FIRE_ERROR(api, t) InvokeWithoutArgs(CreateFunctor(&api, \ |
| &MockCFProxyTraits::FireError, t)) |
| #define API_FIRE_MESSAGE(api, t) InvokeWithoutArgs(CreateFunctor(&api, \ |
| &MockCFProxyTraits::FireMessage, t)) |
| DISABLE_RUNNABLE_METHOD_REFCOUNT(IPC::Channel::Listener); |
| |
| TEST(ChromeProxy, DelegateAddRemove) { |
| StrictMock<MockCFProxyTraits> api; |
| StrictMock<MockChromeProxyDelegate> delegate; |
| StrictMock<MockFactory> factory; // to be destroyed before other mocks |
| CFProxy* proxy = new CFProxy(&api); |
| |
| EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); |
| EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); |
| EXPECT_CALL(api, LaunchApp(_)).WillOnce(Return(true)); |
| EXPECT_CALL(api, CloseChannel(&api.sender)); |
| |
| EXPECT_CALL(delegate, tab_handle()).WillRepeatedly(Return(0)); |
| EXPECT_CALL(delegate, Disconnected()); |
| |
| ProxyParams params; |
| params.profile = "Adam N. Epilinter"; |
| params.timeout = base::TimeDelta::FromSeconds(4); |
| factory.GetProxy(&delegate, params); |
| factory.ReleaseProxy(&delegate, params.profile); |
| } |
| |
| // Not very useful test. Just for illustration. :) |
| TEST(ChromeProxy, SharedProxy) { |
| base::WaitableEvent done1(false, false); |
| base::WaitableEvent done2(false, false); |
| StrictMock<MockCFProxyTraits> api; |
| StrictMock<MockChromeProxyDelegate> delegate1; |
| StrictMock<MockChromeProxyDelegate> delegate2; |
| StrictMock<MockFactory> factory; |
| CFProxy* proxy = new CFProxy(&api); |
| |
| EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); |
| EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); |
| EXPECT_CALL(api, LaunchApp(_)).WillOnce(DoAll( |
| API_FIRE_CONNECT(api, base::TimeDelta::FromMilliseconds(150)), |
| Return(true))); |
| EXPECT_CALL(api, CloseChannel(&api.sender)); |
| |
| EXPECT_CALL(delegate1, tab_handle()).WillRepeatedly(Return(0)); |
| EXPECT_CALL(delegate2, tab_handle()).WillRepeatedly(Return(0)); |
| |
| EXPECT_CALL(delegate1, Connected(proxy)) |
| .WillOnce(InvokeWithoutArgs(&done1, &base::WaitableEvent::Signal)); |
| EXPECT_CALL(delegate2, Connected(proxy)) |
| .WillOnce(InvokeWithoutArgs(&done2, &base::WaitableEvent::Signal)); |
| |
| ProxyParams params; |
| params.profile = "Adam N. Epilinter"; |
| params.timeout = base::TimeDelta::FromSeconds(4); |
| |
| factory.GetProxy(&delegate1, params); |
| params.timeout = base::TimeDelta::FromSeconds(2); |
| factory.GetProxy(&delegate2, params); |
| |
| EXPECT_TRUE(done1.TimedWait(base::TimeDelta::FromSeconds(1))); |
| EXPECT_TRUE(done2.TimedWait(base::TimeDelta::FromSeconds(1))); |
| |
| EXPECT_CALL(delegate2, Disconnected()); |
| EXPECT_CALL(delegate1, Disconnected()); |
| |
| factory.ReleaseProxy(&delegate2, params.profile); |
| factory.ReleaseProxy(&delegate1, params.profile); |
| } |
| |
| TEST(ChromeProxy, LaunchTimeout) { |
| base::WaitableEvent done(true, false); |
| StrictMock<MockCFProxyTraits> api; |
| StrictMock<MockChromeProxyDelegate> delegate; |
| StrictMock<MockFactory> factory; |
| CFProxy* proxy = new CFProxy(&api); |
| |
| EXPECT_CALL(delegate, tab_handle()).WillRepeatedly(Return(0)); |
| EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy)); |
| EXPECT_CALL(api, DoCreateChannel(_, proxy)).WillOnce(Return(&api.sender)); |
| EXPECT_CALL(api, LaunchApp(_)).WillOnce(Return(true)); |
| EXPECT_CALL(api, CloseChannel(&api.sender)); |
| |
| EXPECT_CALL(delegate, PeerLost(_, |
| ChromeProxyDelegate::CHROME_EXE_LAUNCH_TIMEOUT)) |
| .WillOnce(InvokeWithoutArgs(&done, &base::WaitableEvent::Signal)); |
| ProxyParams params; |
| params.profile = "Adam N. Epilinter"; |
| params.timeout = base::TimeDelta::FromMilliseconds(300); |
| factory.GetProxy(&delegate, params); |
| EXPECT_TRUE(done.TimedWait(base::TimeDelta::FromSeconds(1))); |
| |
| EXPECT_CALL(delegate, Disconnected()); |
| factory.ReleaseProxy(&delegate, params.profile); |
| } |
| |
| TEST(ChromeProxy, LaunchChrome) { |
| base::WaitableEvent connected(false, false); |
| StrictMock<MockChromeProxyDelegate> delegate; |
| ChromeProxyFactory factory; |
| |
| ProxyParams params; |
| params.profile = "Adam N. Epilinter"; |
| params.timeout = base::TimeDelta::FromSeconds(10); |
| |
| EXPECT_CALL(delegate, tab_handle()).WillRepeatedly(Return(0)); |
| EXPECT_CALL(delegate, Connected(NotNull())) |
| .WillOnce(InvokeWithoutArgs(&connected, &base::WaitableEvent::Signal)); |
| |
| factory.GetProxy(&delegate, params); |
| EXPECT_TRUE(connected.TimedWait(base::TimeDelta::FromSeconds(15))); |
| |
| EXPECT_CALL(delegate, Disconnected()); |
| factory.ReleaseProxy(&delegate, params.profile); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| namespace { |
| template <typename M, typename A> |
| inline IPC::Message* CreateReply(M* m, const A& a) { |
| IPC::Message* r = IPC::SyncMessage::GenerateReply(m); |
| if (r) { |
| M::WriteReplyParams(r, a); |
| } |
| return r; |
| } |
| |
| template <typename M, typename A, typename B> |
| inline IPC::Message* CreateReply(M* m, const A& a, const B& b) { |
| IPC::Message* r = IPC::SyncMessage::GenerateReply(m); |
| if (r) { |
| M::WriteReplyParams(r, a, b); |
| } |
| return r; |
| } |
| |
| template <typename M, typename A, typename B, typename C> |
| inline IPC::Message* CreateReply(M* m, const A& a, const B& b, const C& c) { |
| IPC::Message* r = IPC::SyncMessage::GenerateReply(m); |
| if (r) { |
| M::WriteReplyParams(r, a, b, c); |
| } |
| return r; |
| } |
| } // namespace |
| |
| DISABLE_RUNNABLE_METHOD_REFCOUNT(SyncMsgSender); |
| TEST(SyncMsgSender, Deserialize) { |
| // Note the ipc thread is not actually needed, but we try to be close |
| // to real-world conditions - that SyncMsgSender works from multiple threads. |
| base::Thread ipc("ipc"); |
| ipc.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0)); |
| |
| StrictMock<MockChromeProxyDelegate> d1; |
| TabsMap tab2delegate; |
| SyncMsgSender queue(&tab2delegate); |
| |
| // Create some sync messages and their replies. |
| AutomationMsg_InstallExtension m1(0, FilePath(L"c:\\awesome.x"), 0); |
| AutomationMsg_CreateExternalTab m2(0, IPC::ExternalTabSettings(), 0, 0, 0); |
| scoped_ptr<IPC::Message> r1(CreateReply(&m1, |
| AUTOMATION_MSG_EXTENSION_INSTALL_SUCCEEDED)); |
| scoped_ptr<IPC::Message> r2(CreateReply(&m2, (HWND)1, (HWND)2, 6)); |
| |
| queue.QueueSyncMessage(&m1, &d1, NULL); |
| queue.QueueSyncMessage(&m2, &d1, NULL); |
| |
| testing::InSequence s; |
| EXPECT_CALL(d1, Completed_InstallExtension(true, |
| AUTOMATION_MSG_EXTENSION_INSTALL_SUCCEEDED, NULL)); |
| EXPECT_CALL(d1, Completed_CreateTab(true, (HWND)1, (HWND)2, 6)); |
| |
| // Execute replies in a worker thread. |
| ipc.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(&queue, |
| &SyncMsgSender::OnReplyReceived, r1.get())); |
| ipc.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(&queue, |
| &SyncMsgSender::OnReplyReceived, r2.get())); |
| ipc.Stop(); |
| |
| // Expect that tab 6 has been associated with the delegate. |
| EXPECT_EQ(&d1, tab2delegate[6]); |
| } |
| |
| TEST(SyncMsgSender, OnChannelClosed) { |
| // TODO(stoyan): implement. |
| } |
| |
| MATCHER_P(EqNavigationInfo, ni, "") { |
| return arg.navigation_type == ni.navigation_type && |
| arg.relative_offset == ni.relative_offset && |
| arg.navigation_index == ni.navigation_index && |
| arg.title == ni.title && |
| arg.url == ni.url && |
| arg.referrer == ni.referrer && |
| arg.security_style == ni.security_style && |
| arg.displayed_insecure_content == ni.displayed_insecure_content && |
| arg.ran_insecure_content == ni.ran_insecure_content; |
| } |
| |
| MATCHER_P(EqMSG, msg, "") { |
| return arg.hwnd == msg.hwnd && |
| arg.message == msg.message && |
| arg.wParam == msg.wParam && |
| arg.lParam == msg.lParam && |
| arg.time == msg.time && |
| arg.pt.x == msg.pt.x && |
| arg.pt.y == msg.pt.y; |
| } |
| |
| MATCHER_P(EqContextMenuParam, p, "") { |
| return arg.screen_x == p.screen_x && |
| arg.screen_y == p.screen_y && |
| arg.link_url == p.link_url && |
| arg.unfiltered_link_url == p.unfiltered_link_url && |
| arg.src_url == p.src_url && |
| arg.page_url == p.page_url && |
| arg.frame_url == p.frame_url; |
| } |
| |
| MATCHER_P(EqURLRequest, p, "") { |
| return arg.url == p.url && |
| arg.method == p.method && |
| arg.referrer == p.referrer && |
| arg.extra_request_headers == p.extra_request_headers && |
| // TODO(stoyan): scoped_refptr<net::UploadData> upload_data; |
| arg.resource_type == p.resource_type; |
| } |
| |
| |
| MATCHER_P(EqAttachExternalTab, p, "") { |
| return arg.cookie == p.cookie && |
| arg.url == p.url && |
| arg.dimensions == p.dimensions && |
| arg.disposition == p.disposition && |
| arg.user_gesture == p.user_gesture && |
| arg.profile_name == p.profile_name; |
| } |
| |
| TEST(Deserialize, DispatchTabMessage) { |
| testing::InSequence s; |
| StrictMock<MockChromeProxyDelegate> delegate; |
| GURL url("http://destination"); |
| GURL ref("http://referer"); |
| |
| // Tuple3<int, int, IPC::NavigationInfo> |
| int flags = 2; |
| IPC::NavigationInfo ni = {2, 3, 4, L"title", url, |
| ref, SECURITY_STYLE_AUTHENTICATION_BROKEN, true, true}; |
| AutomationMsg_NavigationStateChanged m1(0, 1, flags, ni); |
| EXPECT_CALL(delegate, NavigationStateChanged(flags, EqNavigationInfo(ni))); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m1)); |
| |
| // Tuple2<int, std::wstring> |
| AutomationMsg_UpdateTargetUrl m2(0, 1, L"hello"); |
| EXPECT_CALL(delegate, UpdateTargetUrl(StrEq(L"hello"))); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m2)); |
| |
| // Tuple2<int, MSG> |
| MSG wnd_msg = {0, WM_DESTROY, 1, 9, 0x5671, { 11, 12 }}; |
| AutomationMsg_HandleAccelerator m3(0, 1, wnd_msg); |
| EXPECT_CALL(delegate, HandleAccelerator(EqMSG(wnd_msg))); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m3)); |
| |
| // Tuple2<int, bool> |
| AutomationMsg_TabbedOut m4(0, 1, true); |
| EXPECT_CALL(delegate, TabbedOut(true)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m4)); |
| |
| |
| // Tuple4<int, GURL, GURL, int> |
| AutomationMsg_OpenURL m5(0, 1, url, ref, 4); |
| EXPECT_CALL(delegate, OpenURL(url, ref, 4)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m5)); |
| |
| // Tuple3<int, int, GURL> |
| AutomationMsg_NavigationFailed m6(0, 1, 2, url); |
| EXPECT_CALL(delegate, NavigationFailed(2, url)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m6)); |
| |
| |
| // Tuple2<int, IPC::NavigationInfo> |
| AutomationMsg_DidNavigate m7(0, 1, ni); |
| EXPECT_CALL(delegate, DidNavigate(EqNavigationInfo(ni))); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m7)); |
| |
| |
| // Tuple2<int, GURL> |
| AutomationMsg_TabLoaded m8(0, 1, url); |
| EXPECT_CALL(delegate, TabLoaded(url)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m8)); |
| |
| // Tuple4<int, string, string, string> |
| std::string msg("Load oranges barrels"); |
| std::string origin("Brothers Karamazov"); |
| std::string target("Alexander Ivanovich"); |
| AutomationMsg_ForwardMessageToExternalHost m9(0, 1, msg, origin, target); |
| EXPECT_CALL(delegate, MessageToHost(msg, origin, target)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m9)); |
| |
| // Tuple4<int, HANDLE, int, IPC::ContextMenuParams> |
| IPC::ContextMenuParams ctxmenu = { 711, 512, GURL("http://link_src"), |
| GURL("http://unfiltered_link_url"), GURL("http://src_url"), |
| GURL("http://page_url"), GURL("http://frame_url") }; |
| AutomationMsg_ForwardContextMenuToExternalHost m10(0, 1, HANDLE(7), 4, |
| ctxmenu); |
| EXPECT_CALL(delegate, HandleContextMenu(HANDLE(7), 4, |
| EqContextMenuParam(ctxmenu))); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m10)); |
| |
| // Tuple3<int, int, IPC::AutomationURLRequest> |
| IPC::AutomationURLRequest url_request = {"url", "post", "referer", |
| "extra_headers", 0, 3 }; |
| AutomationMsg_RequestStart m11(0, 1, 7, url_request); |
| EXPECT_CALL(delegate, Network_Start(7, EqURLRequest(url_request))); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m11)); |
| |
| // Tuple3<int, int, int> |
| AutomationMsg_RequestRead m12(0, 1, 7, 16384); |
| EXPECT_CALL(delegate, Network_Read(7, 16384)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m12)); |
| |
| // Tuple3<int, int, URLRequestStatus> |
| AutomationMsg_RequestEnd m13(0, 1, 7, URLRequestStatus()); |
| EXPECT_CALL(delegate, Network_End(7, _)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m13)); |
| |
| // Tuple2<int, int> |
| AutomationMsg_DownloadRequestInHost m14(0, 1, 7); |
| EXPECT_CALL(delegate, Network_DownloadInHost(7)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m14)); |
| |
| // Tuple3<int, GURL, string> |
| AutomationMsg_SetCookieAsync m15(0, 1, url, "cake=big"); |
| EXPECT_CALL(delegate, SetCookie(url, "cake=big")); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m15)); |
| |
| // Tuple2<int, IPC::AttachExternalTabParams> |
| IPC::AttachExternalTabParams ext_tab = { 0xFEDCBA0987654321i64, url, |
| gfx::Rect(6, 9, 123, 999), 1, false, "theprofile" }; |
| AutomationMsg_AttachExternalTab m16(0, 1, ext_tab); |
| EXPECT_CALL(delegate, AttachTab(EqAttachExternalTab(ext_tab))); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m16)); |
| |
| // Tuple2<int, int> |
| AutomationMsg_RequestGoToHistoryEntryOffset m17(0, 1, -4); |
| EXPECT_CALL(delegate, GoToHistoryOffset(-4)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m17)); |
| |
| // Tuple3<int, GURL, int> |
| AutomationMsg_GetCookiesFromHost m18(0, 1, url, 903); |
| EXPECT_CALL(delegate, GetCookies(url, 903)); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m18)); |
| |
| AutomationMsg_CloseExternalTab m19(0, 1); |
| EXPECT_CALL(delegate, TabClosed()); |
| EXPECT_TRUE(DispatchTabMessageToDelegate(&delegate, m19)); |
| } |