blob: e320d77690a8bc2ab7bc727a5a015b5d5585bc60 [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/devtools/protocol/cast_handler.h"
#include "chrome/browser/media/router/media_router_factory.h"
#include "chrome/browser/media/router/media_sinks_observer.h"
#include "chrome/browser/media/router/test/mock_media_router.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/common/media_router/media_sink.h"
#include "chrome/common/media_router/media_source_helper.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
using testing::_;
using testing::DoAll;
using testing::Return;
using testing::SaveArg;
using testing::WithArg;
namespace {
constexpr char kRouteId1[] = "route1";
constexpr char kSinkId1[] = "sink1";
constexpr char kSinkId2[] = "sink2";
constexpr char kSinkName1[] = "Sink 1";
constexpr char kSinkName2[] = "Sink 2";
const media_router::MediaSink sink1(kSinkId1,
kSinkName1,
media_router::SinkIconType::CAST);
const media_router::MediaSink sink2(kSinkId2,
kSinkName2,
media_router::SinkIconType::CAST);
const media_router::MediaRoute route1(
kRouteId1,
media_router::MediaSource("https://example.com/"),
kSinkId1,
"",
true,
true);
class MockStartTabMirroringCallback
: public CastHandler::StartTabMirroringCallback {
public:
MOCK_METHOD0(sendSuccess, void());
MOCK_METHOD1(sendFailure, void(const protocol::DispatchResponse&));
MOCK_METHOD0(fallThrough, void());
};
} // namespace
class CastHandlerTest : public ChromeRenderViewHostTestHarness {
public:
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
router_ = static_cast<media_router::MockMediaRouter*>(
media_router::MediaRouterFactory::GetInstance()
->SetTestingFactoryAndUse(
web_contents()->GetBrowserContext(),
base::BindRepeating(&media_router::MockMediaRouter::Create)));
// We cannot use std::make_unique<>() here because we're calling a private
// constructor of CastHandler.
handler_.reset(new CastHandler(web_contents()));
EnableHandler();
}
void TearDown() override {
handler_.reset();
ChromeRenderViewHostTestHarness::TearDown();
}
protected:
void EnableHandler() {
EXPECT_CALL(*router_, RegisterMediaRoutesObserver(_))
.WillOnce(SaveArg<0>(&routes_observer_));
EXPECT_CALL(*router_, RegisterMediaSinksObserver(_))
.WillOnce(DoAll(SaveArg<0>(&sinks_observer_), Return(true)));
handler_->Enable(protocol::Maybe<std::string>());
}
std::unique_ptr<CastHandler> handler_;
media_router::MockMediaRouter* router_ = nullptr;
media_router::MediaRoutesObserver* routes_observer_ = nullptr;
media_router::MediaSinksObserver* sinks_observer_ = nullptr;
};
TEST_F(CastHandlerTest, SetSinkToUse) {
sinks_observer_->OnSinksUpdated({sink1, sink2}, {});
EXPECT_TRUE(handler_->SetSinkToUse(kSinkName1).isSuccess());
const std::string presentation_url("https://example.com/");
content::PresentationRequest request(content::GlobalFrameRoutingId(),
{GURL(presentation_url)}, url::Origin());
// Return sinks when asked for those compatible with |presentation_url|.
EXPECT_CALL(*router_, RegisterMediaSinksObserver(_))
.WillOnce(WithArg<0>(
[presentation_url](media_router::MediaSinksObserver* observer) {
EXPECT_EQ(presentation_url, observer->source().id());
observer->OnSinksUpdated({sink1, sink2}, {});
return true;
}));
EXPECT_CALL(*router_,
CreateRouteInternal(presentation_url, kSinkId1, _, _, _, _, _));
media_router::PresentationServiceDelegateImpl::GetOrCreateForWebContents(
web_contents())
->StartPresentation(request, base::DoNothing(), base::DoNothing());
}
TEST_F(CastHandlerTest, StartTabMirroring) {
sinks_observer_->OnSinksUpdated({sink1, sink2}, {});
auto callback = std::make_unique<MockStartTabMirroringCallback>();
auto* callback_ptr = callback.get();
// Make |router_| return a successful result. |callback| should be notified of
// the success.
EXPECT_CALL(*router_, CreateRouteInternal(
media_router::MediaSourceForTab(
SessionTabHelper::IdForTab(web_contents()).id())
.id(),
kSinkId1, _, _, _, _, _))
.WillOnce(
WithArg<4>([](media_router::MediaRouteResponseCallback& callback) {
std::move(callback).Run(
media_router::mojom::RoutePresentationConnectionPtr(),
media_router::RouteRequestResult(
std::make_unique<media_router::MediaRoute>(route1), "id",
"", media_router::RouteRequestResult::OK));
}));
EXPECT_CALL(*callback_ptr, sendSuccess());
handler_->StartTabMirroring(kSinkName1, std::move(callback));
}
TEST_F(CastHandlerTest, StartTabMirroringWithInvalidName) {
sinks_observer_->OnSinksUpdated({sink1}, {});
auto callback = std::make_unique<MockStartTabMirroringCallback>();
auto* callback_ptr = callback.get();
// Attempting to start casting with a name different from that of the
// discovered sink should fail.
EXPECT_CALL(*callback_ptr, sendFailure(_));
handler_->StartTabMirroring(kSinkName2, std::move(callback));
}
TEST_F(CastHandlerTest, StopCasting) {
sinks_observer_->OnSinksUpdated({sink1, sink2}, {});
routes_observer_->OnRoutesUpdated({route1}, {});
EXPECT_CALL(*router_, TerminateRoute(kRouteId1));
EXPECT_TRUE(handler_->StopCasting(kSinkName1).isSuccess());
}
TEST_F(CastHandlerTest, StopCastingWithInvalidName) {
sinks_observer_->OnSinksUpdated({sink1, sink2}, {});
routes_observer_->OnRoutesUpdated({route1}, {});
// Attempting to stop casting to a sink without a route should fail.
EXPECT_EQ(protocol::Response::kError,
handler_->StopCasting(kSinkName2).status());
}