blob: c7fe639c7880eb3aef283c4881ee86d2f0e3e487 [file] [log] [blame]
// Copyright 2015 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 "content/browser/presentation/presentation_service_impl.h"
#include <stddef.h>
#include <stdint.h>
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/location.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/presentation_service_delegate.h"
#include "content/public/common/presentation_connection_message.h"
#include "content/public/common/presentation_info.h"
#include "content/test/test_render_frame_host.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
using ::testing::ByRef;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::Mock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::WithArgs;
namespace content {
namespace {
// Matches content::PresentationInfo.
MATCHER_P(InfoEquals, expected, "") {
return expected.presentation_url == arg.presentation_url &&
expected.presentation_id == arg.presentation_id;
}
const char kPresentationId[] = "presentationId";
const char kPresentationUrl1[] = "http://foo.com/index.html";
const char kPresentationUrl2[] = "http://example.com/index.html";
const char kPresentationUrl3[] = "http://example.net/index.html";
void DoNothing(const base::Optional<content::PresentationInfo>& info,
const base::Optional<content::PresentationError>& error) {}
} // namespace
class MockPresentationServiceDelegate
: public ControllerPresentationServiceDelegate {
public:
MOCK_METHOD3(AddObserver,
void(int render_process_id,
int render_frame_id,
PresentationServiceDelegate::Observer* observer));
MOCK_METHOD2(RemoveObserver,
void(int render_process_id, int render_frame_id));
bool AddScreenAvailabilityListener(
int render_process_id,
int routing_id,
PresentationScreenAvailabilityListener* listener) override {
if (!screen_availability_listening_supported_)
listener->OnScreenAvailabilityNotSupported();
return AddScreenAvailabilityListener();
}
MOCK_METHOD0(AddScreenAvailabilityListener, bool());
MOCK_METHOD3(RemoveScreenAvailabilityListener,
void(int render_process_id,
int routing_id,
PresentationScreenAvailabilityListener* listener));
MOCK_METHOD2(Reset,
void(int render_process_id,
int routing_id));
MOCK_METHOD4(SetDefaultPresentationUrls,
void(int render_process_id,
int routing_id,
const std::vector<GURL>& default_presentation_urls,
const PresentationConnectionCallback& callback));
MOCK_METHOD5(StartPresentation,
void(int render_process_id,
int render_frame_id,
const std::vector<GURL>& presentation_urls,
const PresentationConnectionCallback& success_cb,
const PresentationConnectionErrorCallback& error_cb));
MOCK_METHOD6(ReconnectPresentation,
void(int render_process_id,
int render_frame_id,
const std::vector<GURL>& presentation_urls,
const std::string& presentation_id,
const PresentationConnectionCallback& success_cb,
const PresentationConnectionErrorCallback& error_cb));
MOCK_METHOD3(CloseConnection,
void(int render_process_id,
int render_frame_id,
const std::string& presentation_id));
MOCK_METHOD3(Terminate,
void(int render_process_id,
int render_frame_id,
const std::string& presentation_id));
MOCK_METHOD4(ListenForConnectionMessages,
void(int render_process_id,
int render_frame_id,
const PresentationInfo& presentation_info,
const PresentationConnectionMessageCallback& message_cb));
// PresentationConnectionMessage is move-only.
void SendMessage(int render_process_id,
int render_frame_id,
const PresentationInfo& presentation_info,
PresentationConnectionMessage message,
const SendMessageCallback& send_message_cb) {
SendMessageInternal(render_process_id, render_frame_id, presentation_info,
message, send_message_cb);
}
MOCK_METHOD5(SendMessageInternal,
void(int render_process_id,
int render_frame_id,
const PresentationInfo& presentation_info,
const PresentationConnectionMessage& message,
const SendMessageCallback& send_message_cb));
MOCK_METHOD4(
ListenForConnectionStateChange,
void(int render_process_id,
int render_frame_id,
const PresentationInfo& connection,
const PresentationConnectionStateChangedCallback& state_changed_cb));
void ConnectToPresentation(
int render_process_id,
int render_frame_id,
const PresentationInfo& presentation_info,
PresentationConnectionPtr controller_conn_ptr,
PresentationConnectionRequest receiver_conn_request) override {
RegisterOffscreenPresentationConnectionRaw(
render_process_id, render_frame_id, presentation_info,
controller_conn_ptr.get());
}
MOCK_METHOD4(RegisterOffscreenPresentationConnectionRaw,
void(int render_process_id,
int render_frame_id,
const PresentationInfo& presentation_info,
blink::mojom::PresentationConnection* connection));
void set_screen_availability_listening_supported(bool value) {
screen_availability_listening_supported_ = value;
}
private:
bool screen_availability_listening_supported_ = true;
};
class MockReceiverPresentationServiceDelegate
: public ReceiverPresentationServiceDelegate {
public:
MOCK_METHOD3(AddObserver,
void(int render_process_id,
int render_frame_id,
PresentationServiceDelegate::Observer* observer));
MOCK_METHOD2(RemoveObserver,
void(int render_process_id, int render_frame_id));
MOCK_METHOD2(Reset, void(int render_process_id, int routing_id));
MOCK_METHOD1(RegisterReceiverConnectionAvailableCallback,
void(const ReceiverConnectionAvailableCallback&));
};
class MockPresentationConnection : public blink::mojom::PresentationConnection {
public:
// PresentationConnectionMessage is move-only.
void OnMessage(PresentationConnectionMessage message,
const base::Callback<void(bool)>& send_message_cb) {
OnMessageInternal(message, send_message_cb);
}
MOCK_METHOD2(OnMessageInternal,
void(const PresentationConnectionMessage& message,
const base::Callback<void(bool)>& send_message_cb));
MOCK_METHOD1(DidChangeState, void(PresentationConnectionState state));
MOCK_METHOD0(OnClose, void());
};
class MockPresentationServiceClient
: public blink::mojom::PresentationServiceClient {
public:
MOCK_METHOD2(OnScreenAvailabilityUpdated,
void(const GURL& url, bool available));
MOCK_METHOD2(OnConnectionStateChanged,
void(const PresentationInfo& connection,
PresentationConnectionState new_state));
MOCK_METHOD3(OnConnectionClosed,
void(const PresentationInfo& connection,
PresentationConnectionCloseReason reason,
const std::string& message));
MOCK_METHOD1(OnScreenAvailabilityNotSupported, void(const GURL& url));
// PresentationConnectionMessage is move-only.
void OnConnectionMessagesReceived(
const PresentationInfo& presentation_info,
std::vector<PresentationConnectionMessage> messages) {
OnConnectionMessagesReceivedInternal(presentation_info, messages);
}
MOCK_METHOD2(
OnConnectionMessagesReceivedInternal,
void(const PresentationInfo& presentation_info,
const std::vector<PresentationConnectionMessage>& messages));
MOCK_METHOD1(OnDefaultPresentationStarted,
void(const PresentationInfo& presentation_info));
void OnReceiverConnectionAvailable(
const PresentationInfo& presentation_info,
blink::mojom::PresentationConnectionPtr controller_conn_ptr,
blink::mojom::PresentationConnectionRequest receiver_conn_request)
override {
OnReceiverConnectionAvailable(presentation_info);
}
MOCK_METHOD1(OnReceiverConnectionAvailable,
void(const PresentationInfo& presentation_info));
};
class PresentationServiceImplTest : public RenderViewHostImplTestHarness {
public:
PresentationServiceImplTest()
: presentation_url1_(GURL(kPresentationUrl1)),
presentation_url2_(GURL(kPresentationUrl2)),
presentation_url3_(GURL(kPresentationUrl3)) {}
void SetUp() override {
RenderViewHostImplTestHarness::SetUp();
// This needed to keep the WebContentsObserverSanityChecker checks happy for
// when AppendChild is called.
NavigateAndCommit(GURL("about:blank"));
auto request = mojo::MakeRequest(&service_ptr_);
EXPECT_CALL(mock_delegate_, AddObserver(_, _, _)).Times(1);
TestRenderFrameHost* render_frame_host = contents()->GetMainFrame();
render_frame_host->InitializeRenderFrameIfNeeded();
service_impl_.reset(new PresentationServiceImpl(
render_frame_host, contents(), &mock_delegate_, nullptr));
service_impl_->Bind(std::move(request));
blink::mojom::PresentationServiceClientPtr client_ptr;
client_binding_.reset(
new mojo::Binding<blink::mojom::PresentationServiceClient>(
&mock_client_, mojo::MakeRequest(&client_ptr)));
service_impl_->SetClient(std::move(client_ptr));
presentation_urls_.push_back(presentation_url1_);
presentation_urls_.push_back(presentation_url2_);
}
void TearDown() override {
service_ptr_.reset();
if (service_impl_.get()) {
EXPECT_CALL(mock_delegate_, RemoveObserver(_, _)).Times(1);
service_impl_.reset();
}
RenderViewHostImplTestHarness::TearDown();
}
void Navigate(bool main_frame) {
RenderFrameHost* rfh = main_rfh();
RenderFrameHostTester* rfh_tester = RenderFrameHostTester::For(rfh);
if (!main_frame)
rfh = rfh_tester->AppendChild("subframe");
std::unique_ptr<NavigationHandle> navigation_handle =
NavigationHandle::CreateNavigationHandleForTesting(
GURL(), rfh, true);
// Destructor calls DidFinishNavigation.
}
void ListenForScreenAvailabilityAndWait(const GURL& url,
bool delegate_success) {
base::RunLoop run_loop;
// This will call to |service_impl_| via mojo. Process the message
// using RunLoop.
// The callback shouldn't be invoked since there is no availability
// result yet.
EXPECT_CALL(mock_delegate_, AddScreenAvailabilityListener())
.WillOnce(DoAll(
InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
Return(delegate_success)));
service_ptr_->ListenForScreenAvailability(url);
run_loop.Run();
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_));
}
void RunLoopFor(base::TimeDelta duration) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), duration);
run_loop.Run();
}
void SaveQuitClosureAndRunLoop() {
base::RunLoop run_loop;
run_loop_quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
run_loop_quit_closure_.Reset();
}
void SimulateScreenAvailabilityChangeAndWait(const GURL& url,
bool available) {
auto listener_it = service_impl_->screen_availability_listeners_.find(url);
ASSERT_TRUE(listener_it->second);
base::RunLoop run_loop;
EXPECT_CALL(mock_client_, OnScreenAvailabilityUpdated(url, available))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
listener_it->second->OnScreenAvailabilityChanged(available);
run_loop.Run();
}
void ExpectReset() {
EXPECT_CALL(mock_delegate_, Reset(_, _)).Times(1);
}
void ExpectCleanState() {
EXPECT_TRUE(service_impl_->default_presentation_urls_.empty());
EXPECT_EQ(
service_impl_->screen_availability_listeners_.find(presentation_url1_),
service_impl_->screen_availability_listeners_.end());
}
void ExpectNewPresentationCallbackSuccess(
const base::Optional<PresentationInfo>& info,
const base::Optional<PresentationError>& error) {
EXPECT_TRUE(info);
EXPECT_FALSE(error);
if (!run_loop_quit_closure_.is_null())
run_loop_quit_closure_.Run();
}
void ExpectNewPresentationCallbackError(
const base::Optional<PresentationInfo>& info,
const base::Optional<PresentationError>& error) {
EXPECT_FALSE(info);
EXPECT_TRUE(error);
if (!run_loop_quit_closure_.is_null())
run_loop_quit_closure_.Run();
}
void ExpectConnectionMessages(
const std::vector<PresentationConnectionMessage>& expected_msgs,
const std::vector<PresentationConnectionMessage>& actual_msgs) {
EXPECT_EQ(expected_msgs.size(), actual_msgs.size());
for (size_t i = 0; i < actual_msgs.size(); ++i)
EXPECT_EQ(expected_msgs[i], actual_msgs[i]);
}
MockPresentationServiceDelegate mock_delegate_;
MockReceiverPresentationServiceDelegate mock_receiver_delegate_;
std::unique_ptr<PresentationServiceImpl> service_impl_;
mojo::InterfacePtr<blink::mojom::PresentationService> service_ptr_;
MockPresentationServiceClient mock_client_;
std::unique_ptr<mojo::Binding<blink::mojom::PresentationServiceClient>>
client_binding_;
base::Closure run_loop_quit_closure_;
GURL presentation_url1_;
GURL presentation_url2_;
GURL presentation_url3_;
std::vector<GURL> presentation_urls_;
};
TEST_F(PresentationServiceImplTest, ListenForScreenAvailability) {
ListenForScreenAvailabilityAndWait(presentation_url1_, true);
SimulateScreenAvailabilityChangeAndWait(presentation_url1_, true);
SimulateScreenAvailabilityChangeAndWait(presentation_url1_, false);
SimulateScreenAvailabilityChangeAndWait(presentation_url1_, true);
}
TEST_F(PresentationServiceImplTest, Reset) {
ListenForScreenAvailabilityAndWait(presentation_url1_, true);
ExpectReset();
service_impl_->Reset();
ExpectCleanState();
}
TEST_F(PresentationServiceImplTest, DidNavigateThisFrame) {
ListenForScreenAvailabilityAndWait(presentation_url1_, true);
ExpectReset();
Navigate(true);
ExpectCleanState();
}
TEST_F(PresentationServiceImplTest, DidNavigateOtherFrame) {
ListenForScreenAvailabilityAndWait(presentation_url1_, true);
Navigate(false);
// Availability is reported and callback is invoked since it was not
// removed.
SimulateScreenAvailabilityChangeAndWait(presentation_url1_, true);
}
TEST_F(PresentationServiceImplTest, ThisRenderFrameDeleted) {
ListenForScreenAvailabilityAndWait(presentation_url1_, true);
ExpectReset();
// Since the frame matched the service, |service_impl_| will be deleted.
PresentationServiceImpl* service = service_impl_.release();
EXPECT_CALL(mock_delegate_, RemoveObserver(_, _)).Times(1);
service->RenderFrameDeleted(contents()->GetMainFrame());
}
TEST_F(PresentationServiceImplTest, OtherRenderFrameDeleted) {
ListenForScreenAvailabilityAndWait(presentation_url1_, true);
// TODO(imcheng): How to get a different RenderFrameHost?
service_impl_->RenderFrameDeleted(nullptr);
// Availability is reported and callback should be invoked since listener
// has not been deleted.
SimulateScreenAvailabilityChangeAndWait(presentation_url1_, true);
}
TEST_F(PresentationServiceImplTest, DelegateFails) {
ListenForScreenAvailabilityAndWait(presentation_url1_, false);
ASSERT_EQ(
service_impl_->screen_availability_listeners_.find(presentation_url1_),
service_impl_->screen_availability_listeners_.end());
}
TEST_F(PresentationServiceImplTest, SetDefaultPresentationUrls) {
EXPECT_CALL(mock_delegate_,
SetDefaultPresentationUrls(_, _, presentation_urls_, _))
.Times(1);
service_impl_->SetDefaultPresentationUrls(presentation_urls_);
// Sets different DPUs.
std::vector<GURL> more_urls = presentation_urls_;
more_urls.push_back(presentation_url3_);
PresentationConnectionCallback callback;
EXPECT_CALL(mock_delegate_, SetDefaultPresentationUrls(_, _, more_urls, _))
.WillOnce(SaveArg<3>(&callback));
service_impl_->SetDefaultPresentationUrls(more_urls);
PresentationInfo presentation_info(presentation_url2_, kPresentationId);
base::RunLoop run_loop;
EXPECT_CALL(mock_client_,
OnDefaultPresentationStarted(InfoEquals(presentation_info)))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _));
callback.Run(PresentationInfo(presentation_url2_, kPresentationId));
run_loop.Run();
}
TEST_F(PresentationServiceImplTest, ListenForConnectionStateChange) {
PresentationInfo connection(presentation_url1_, kPresentationId);
PresentationConnectionStateChangedCallback state_changed_cb;
// Trigger state change. It should be propagated back up to |mock_client_|.
PresentationInfo presentation_connection(presentation_url1_, kPresentationId);
EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _))
.WillOnce(SaveArg<3>(&state_changed_cb));
service_impl_->ListenForConnectionStateChange(connection);
{
base::RunLoop run_loop;
EXPECT_CALL(mock_client_, OnConnectionStateChanged(
InfoEquals(presentation_connection),
PRESENTATION_CONNECTION_STATE_TERMINATED))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
state_changed_cb.Run(PresentationConnectionStateChangeInfo(
PRESENTATION_CONNECTION_STATE_TERMINATED));
run_loop.Run();
}
}
TEST_F(PresentationServiceImplTest, ListenForConnectionClose) {
PresentationInfo connection(presentation_url1_, kPresentationId);
PresentationConnectionStateChangedCallback state_changed_cb;
EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _))
.WillOnce(SaveArg<3>(&state_changed_cb));
service_impl_->ListenForConnectionStateChange(connection);
// Trigger connection close. It should be propagated back up to
// |mock_client_|.
PresentationInfo presentation_connection(presentation_url1_, kPresentationId);
{
base::RunLoop run_loop;
PresentationConnectionStateChangeInfo closed_info(
PRESENTATION_CONNECTION_STATE_CLOSED);
closed_info.close_reason = PRESENTATION_CONNECTION_CLOSE_REASON_WENT_AWAY;
closed_info.message = "Foo";
EXPECT_CALL(mock_client_,
OnConnectionClosed(
InfoEquals(presentation_connection),
PRESENTATION_CONNECTION_CLOSE_REASON_WENT_AWAY, "Foo"))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
state_changed_cb.Run(closed_info);
run_loop.Run();
}
}
TEST_F(PresentationServiceImplTest, SetSameDefaultPresentationUrls) {
EXPECT_CALL(mock_delegate_,
SetDefaultPresentationUrls(_, _, presentation_urls_, _))
.Times(1);
service_impl_->SetDefaultPresentationUrls(presentation_urls_);
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_));
// Same URLs as before; no-ops.
service_impl_->SetDefaultPresentationUrls(presentation_urls_);
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_));
}
TEST_F(PresentationServiceImplTest, StartPresentationSuccess) {
service_ptr_->StartPresentation(
presentation_urls_,
base::Bind(
&PresentationServiceImplTest::ExpectNewPresentationCallbackSuccess,
base::Unretained(this)));
base::RunLoop run_loop;
base::Callback<void(const PresentationInfo&)> success_cb;
EXPECT_CALL(mock_delegate_, StartPresentation(_, _, presentation_urls_, _, _))
.WillOnce(DoAll(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
SaveArg<3>(&success_cb)));
run_loop.Run();
EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _))
.Times(1);
success_cb.Run(PresentationInfo(presentation_url1_, kPresentationId));
SaveQuitClosureAndRunLoop();
}
TEST_F(PresentationServiceImplTest, StartPresentationError) {
service_ptr_->StartPresentation(
presentation_urls_,
base::Bind(
&PresentationServiceImplTest::ExpectNewPresentationCallbackError,
base::Unretained(this)));
base::RunLoop run_loop;
base::Callback<void(const PresentationError&)> error_cb;
EXPECT_CALL(mock_delegate_, StartPresentation(_, _, presentation_urls_, _, _))
.WillOnce(DoAll(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
SaveArg<4>(&error_cb)));
run_loop.Run();
error_cb.Run(PresentationError(PRESENTATION_ERROR_UNKNOWN, "Error message"));
SaveQuitClosureAndRunLoop();
}
TEST_F(PresentationServiceImplTest, ReconnectPresentationSuccess) {
service_ptr_->ReconnectPresentation(
presentation_urls_, base::Optional<std::string>(kPresentationId),
base::Bind(
&PresentationServiceImplTest::ExpectNewPresentationCallbackSuccess,
base::Unretained(this)));
base::RunLoop run_loop;
base::Callback<void(const PresentationInfo&)> success_cb;
EXPECT_CALL(mock_delegate_, ReconnectPresentation(_, _, presentation_urls_,
kPresentationId, _, _))
.WillOnce(DoAll(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
SaveArg<4>(&success_cb)));
run_loop.Run();
EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _))
.Times(1);
success_cb.Run(PresentationInfo(presentation_url1_, kPresentationId));
SaveQuitClosureAndRunLoop();
}
TEST_F(PresentationServiceImplTest, ReconnectPresentationError) {
service_ptr_->ReconnectPresentation(
presentation_urls_, base::Optional<std::string>(kPresentationId),
base::Bind(
&PresentationServiceImplTest::ExpectNewPresentationCallbackError,
base::Unretained(this)));
base::RunLoop run_loop;
base::Callback<void(const PresentationError&)> error_cb;
EXPECT_CALL(mock_delegate_, ReconnectPresentation(_, _, presentation_urls_,
kPresentationId, _, _))
.WillOnce(DoAll(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
SaveArg<5>(&error_cb)));
run_loop.Run();
error_cb.Run(PresentationError(PRESENTATION_ERROR_UNKNOWN, "Error message"));
SaveQuitClosureAndRunLoop();
}
TEST_F(PresentationServiceImplTest, CloseConnection) {
service_ptr_->CloseConnection(presentation_url1_, kPresentationId);
base::RunLoop run_loop;
EXPECT_CALL(mock_delegate_, CloseConnection(_, _, Eq(kPresentationId)))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
run_loop.Run();
}
TEST_F(PresentationServiceImplTest, Terminate) {
service_ptr_->Terminate(presentation_url1_, kPresentationId);
base::RunLoop run_loop;
EXPECT_CALL(mock_delegate_, Terminate(_, _, Eq(kPresentationId)))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
run_loop.Run();
}
TEST_F(PresentationServiceImplTest, SetPresentationConnection) {
PresentationInfo presentation_info(presentation_url1_, kPresentationId);
blink::mojom::PresentationConnectionPtr connection;
MockPresentationConnection mock_presentation_connection;
mojo::Binding<blink::mojom::PresentationConnection> connection_binding(
&mock_presentation_connection, mojo::MakeRequest(&connection));
blink::mojom::PresentationConnectionPtr receiver_connection;
auto request = mojo::MakeRequest(&receiver_connection);
PresentationInfo expected(presentation_url1_, kPresentationId);
EXPECT_CALL(mock_delegate_, RegisterOffscreenPresentationConnectionRaw(
_, _, InfoEquals(expected), _));
service_impl_->SetPresentationConnection(
presentation_info, std::move(connection), std::move(request));
}
TEST_F(PresentationServiceImplTest, ReceiverPresentationServiceDelegate) {
MockReceiverPresentationServiceDelegate mock_receiver_delegate;
PresentationServiceImpl service_impl(contents()->GetMainFrame(), contents(),
nullptr, &mock_receiver_delegate);
ReceiverConnectionAvailableCallback callback;
EXPECT_CALL(mock_receiver_delegate,
RegisterReceiverConnectionAvailableCallback(_))
.WillOnce(SaveArg<0>(&callback));
blink::mojom::PresentationServiceClientPtr client_ptr;
client_binding_.reset(
new mojo::Binding<blink::mojom::PresentationServiceClient>(
&mock_client_, mojo::MakeRequest(&client_ptr)));
service_impl.controller_delegate_ = nullptr;
service_impl.SetClient(std::move(client_ptr));
EXPECT_FALSE(callback.is_null());
// NO-OP for ControllerPresentationServiceDelegate API functions
EXPECT_CALL(mock_delegate_, ListenForConnectionMessages(_, _, _, _)).Times(0);
PresentationInfo presentation_info(presentation_url1_, kPresentationId);
service_impl.ListenForConnectionMessages(presentation_info);
}
TEST_F(PresentationServiceImplTest, StartPresentationInProgress) {
EXPECT_CALL(mock_delegate_, StartPresentation(_, _, presentation_urls_, _, _))
.Times(1);
service_ptr_->StartPresentation(presentation_urls_, base::Bind(&DoNothing));
// This request should fail immediately, since there is already a
// StartPresentation in progress.
service_ptr_->StartPresentation(
presentation_urls_,
base::Bind(
&PresentationServiceImplTest::ExpectNewPresentationCallbackError,
base::Unretained(this)));
SaveQuitClosureAndRunLoop();
}
TEST_F(PresentationServiceImplTest, MaxPendingReconnectPresentationRequests) {
const char* presentation_url = "http://fooUrl%d";
const char* presentation_id = "presentationId%d";
int num_requests = PresentationServiceImpl::kMaxQueuedRequests;
int i = 0;
EXPECT_CALL(mock_delegate_, ReconnectPresentation(_, _, _, _, _, _))
.Times(num_requests);
for (; i < num_requests; ++i) {
std::vector<GURL> urls = {GURL(base::StringPrintf(presentation_url, i))};
service_ptr_->ReconnectPresentation(
urls, base::StringPrintf(presentation_id, i), base::Bind(&DoNothing));
}
std::vector<GURL> urls = {GURL(base::StringPrintf(presentation_url, i))};
// Exceeded maximum queue size, should invoke mojo callback with error.
service_ptr_->ReconnectPresentation(
urls, base::StringPrintf(presentation_id, i),
base::Bind(
&PresentationServiceImplTest::ExpectNewPresentationCallbackError,
base::Unretained(this)));
SaveQuitClosureAndRunLoop();
}
TEST_F(PresentationServiceImplTest, ScreenAvailabilityNotSupported) {
mock_delegate_.set_screen_availability_listening_supported(false);
base::RunLoop run_loop;
EXPECT_CALL(mock_client_,
OnScreenAvailabilityNotSupported(presentation_url1_))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
ListenForScreenAvailabilityAndWait(presentation_url1_, false);
run_loop.Run();
}
} // namespace content