Commit aef92ff6 authored by John Williams's avatar John Williams Committed by Commit Bot

[Cast MRP] Added unit tests for MirroringActivityRecord.

This change also factors out functionality from CastActivityRecordTest
into ActivityRecordTestBase so it can be shared.

Bug: b/155649299
Change-Id: I84ff6c2527e7bcb0da53ffa86078f1c09278b6cd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2244125
Auto-Submit: John Williams <jrw@chromium.org>
Reviewed-by: default avatarmark a. foltz <mfoltz@chromium.org>
Reviewed-by: default avatarTakumi Fujimoto <takumif@chromium.org>
Commit-Queue: John Williams <jrw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#781389}
parent 1c7f988e
...@@ -258,6 +258,8 @@ source_set("unittests") { ...@@ -258,6 +258,8 @@ source_set("unittests") {
"mojo/media_router_mojo_impl_unittest.cc", "mojo/media_router_mojo_impl_unittest.cc",
"mojo/media_router_mojo_metrics_unittest.cc", "mojo/media_router_mojo_metrics_unittest.cc",
"mojo/media_sink_service_status_unittest.cc", "mojo/media_sink_service_status_unittest.cc",
"providers/cast/activity_record_test_base.cc",
"providers/cast/activity_record_test_base.h",
"providers/cast/cast_activity_manager_unittest.cc", "providers/cast/cast_activity_manager_unittest.cc",
"providers/cast/cast_activity_record_unittest.cc", "providers/cast/cast_activity_record_unittest.cc",
"providers/cast/cast_app_availability_tracker_unittest.cc", "providers/cast/cast_app_availability_tracker_unittest.cc",
......
// Copyright 2020 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/media/router/providers/cast/activity_record_test_base.h"
#include <algorithm>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/test/bind_test_util.h"
#include "base/test/mock_callback.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "chrome/browser/media/router/data_decoder_util.h"
#include "chrome/browser/media/router/providers/cast/cast_activity_manager.h"
#include "chrome/browser/media/router/providers/cast/cast_session_client.h"
#include "chrome/browser/media/router/providers/cast/test_util.h"
#include "chrome/browser/media/router/test/test_helper.h"
#include "chrome/common/media_router/test/test_helper.h"
#include "components/cast_channel/cast_test_util.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::test::ParseJson;
namespace media_router {
MockCastSessionClient::MockCastSessionClient(const std::string& client_id,
const url::Origin& origin,
int tab_id)
: CastSessionClient(client_id, origin, tab_id) {
instances_.push_back(this);
}
MockCastSessionClient::~MockCastSessionClient() {
base::Erase(instances_, this);
}
std::vector<MockCastSessionClient*> MockCastSessionClient::instances_;
MockCastActivityManager::MockCastActivityManager() = default;
MockCastActivityManager::~MockCastActivityManager() = default;
const char* const ActivityRecordTestBase::kAppId = "theAppId";
const char* const ActivityRecordTestBase::kRouteId = "theRouteId";
const char* const ActivityRecordTestBase::kSinkId = "cast:<id42>";
const char* const ActivityRecordTestBase::kHashToken = "dummyHashToken";
ActivityRecordTestBase::ActivityRecordTestBase() = default;
ActivityRecordTestBase::~ActivityRecordTestBase() = default;
void ActivityRecordTestBase::SetUp() {
ASSERT_TRUE(MockCastSessionClient::instances().empty());
media_sink_service_.AddOrUpdateSink(sink_);
ASSERT_EQ(kSinkId, sink_.id());
media_sink_service_.AddOrUpdateSink(sink_);
ASSERT_EQ(kSinkId, sink_.id());
ActivityRecord::SetClientFactoryForTest(this);
std::unique_ptr<CastSession> session = CastSession::From(sink_, ParseJson(R"({
"applications": [{
"appId": "theAppId",
"displayName": "App display name",
"namespaces": [
{"name": "urn:x-cast:com.google.cast.media"},
{"name": "urn:x-cast:com.google.foo"}
],
"sessionId": "theSessionId",
"statusText": "theStatusText",
"transportId": "theTransportId"
}]
})"));
ASSERT_EQ("theSessionId", session->session_id());
session_ = session.get();
session_tracker_.SetSessionForTest(kSinkId, std::move(session));
}
void ActivityRecordTestBase::TearDown() {
RunUntilIdle();
ActivityRecord::SetClientFactoryForTest(nullptr);
}
void ActivityRecordTestBase::RunUntilIdle() {
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&socket_service_);
testing::Mock::VerifyAndClearExpectations(&message_handler_);
testing::Mock::VerifyAndClearExpectations(&manager_);
for (const auto* client : MockCastSessionClient::instances())
testing::Mock::VerifyAndClearExpectations(&client);
}
std::unique_ptr<CastSessionClient> ActivityRecordTestBase::MakeClientForTest(
const std::string& client_id,
const url::Origin& origin,
int tab_id) {
return std::make_unique<MockCastSessionClient>(client_id, origin, tab_id);
}
} // namespace media_router
// Copyright 2020 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.
#ifndef CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_ACTIVITY_RECORD_TEST_BASE_H_
#define CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_ACTIVITY_RECORD_TEST_BASE_H_
#include "chrome/browser/media/router/providers/cast/activity_record.h"
#include "chrome/browser/media/router/providers/cast/cast_activity_manager.h"
#include "chrome/browser/media/router/providers/cast/cast_internal_message_util.h"
#include "chrome/browser/media/router/providers/cast/cast_session_client.h"
#include "chrome/browser/media/router/providers/cast/cast_session_tracker.h"
#include "chrome/browser/media/router/test/test_helper.h"
#include "chrome/common/media_router/discovery/media_sink_internal.h"
#include "chrome/common/media_router/media_route.h"
#include "chrome/common/media_router/test/test_helper.h"
#include "components/cast_channel/cast_test_util.h"
#include "content/public/test/browser_task_environment.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media_router {
class MockCastSessionClient : public CastSessionClient {
public:
MockCastSessionClient(const std::string& client_id,
const url::Origin& origin,
int tab_id);
~MockCastSessionClient() override;
static const std::vector<MockCastSessionClient*>& instances() {
return instances_;
}
MOCK_METHOD0(Init, mojom::RoutePresentationConnectionPtr());
MOCK_METHOD1(SendMessageToClient,
void(blink::mojom::PresentationConnectionMessagePtr message));
MOCK_METHOD2(SendMediaStatusToClient,
void(const base::Value& media_status,
base::Optional<int> request_id));
MOCK_METHOD1(
CloseConnection,
void(blink::mojom::PresentationConnectionCloseReason close_reason));
MOCK_METHOD0(TerminateConnection, void());
MOCK_CONST_METHOD2(MatchesAutoJoinPolicy,
bool(url::Origin origin, int tab_id));
MOCK_METHOD3(SendErrorCodeToClient,
void(int sequence_number,
CastInternalMessage::ErrorCode error_code,
base::Optional<std::string> description));
MOCK_METHOD2(SendErrorToClient, void(int sequence_number, base::Value error));
MOCK_METHOD1(OnMessage,
void(blink::mojom::PresentationConnectionMessagePtr message));
MOCK_METHOD1(DidChangeState,
void(blink::mojom::PresentationConnectionState state));
MOCK_METHOD1(DidClose,
void(blink::mojom::PresentationConnectionCloseReason reason));
private:
static std::vector<MockCastSessionClient*> instances_;
};
class MockCastActivityManager : public CastActivityManagerBase {
public:
MockCastActivityManager();
~MockCastActivityManager();
MOCK_METHOD2(MakeResultCallbackForRoute,
cast_channel::ResultCallback(
const std::string& route_id,
mojom::MediaRouteProvider::TerminateRouteCallback callback));
};
// Base class for testing subclasses of ActivityRecord.
class ActivityRecordTestBase : public testing::Test,
public CastSessionClientFactoryForTest {
protected:
static constexpr int kChannelId = 42;
static const char* const kAppId;
static const char* const kRouteId;
static const char* const kSinkId;
static const char* const kHashToken;
ActivityRecordTestBase();
~ActivityRecordTestBase() override;
void SetUp() override;
void TearDown() override;
// Run any pending events and verify expectations associated with them.
void RunUntilIdle();
// from CastSessionClientFactoryForTest
std::unique_ptr<CastSessionClient> MakeClientForTest(
const std::string& client_id,
const url::Origin& origin,
int tab_id) override;
// TODO(crbug.com/954797): Factor out members also present in
// CastActivityManagerTest.
content::BrowserTaskEnvironment task_environment_;
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
TestMediaSinkService media_sink_service_;
cast_channel::MockCastSocketService socket_service_{
task_environment_.GetMainThreadTaskRunner()};
cast_channel::MockCastMessageHandler message_handler_{&socket_service_};
CastSessionTracker session_tracker_{&media_sink_service_, &message_handler_,
socket_service_.task_runner()};
MediaSinkInternal sink_ = CreateCastSink(kChannelId);
MockCastActivityManager manager_;
CastSession* session_ = nullptr;
};
} // namespace media_router
#endif // CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_ACTIVITY_RECORD_TEST_BASE_H_
...@@ -58,9 +58,9 @@ class CastSessionTracker : public MediaSinkServiceBase::Observer, ...@@ -58,9 +58,9 @@ class CastSessionTracker : public MediaSinkServiceBase::Observer,
private: private:
friend class CastSessionTrackerTest; friend class CastSessionTrackerTest;
friend class CastActivityRecordTest;
friend class CastActivityManagerTest; friend class CastActivityManagerTest;
friend class CastMediaRouteProviderTest; friend class CastMediaRouteProviderTest;
friend class ActivityRecordTestBase;
// Use |GetInstance()| instead. // Use |GetInstance()| instead.
CastSessionTracker( CastSessionTracker(
......
...@@ -141,9 +141,10 @@ MirroringActivityRecord::~MirroringActivityRecord() { ...@@ -141,9 +141,10 @@ MirroringActivityRecord::~MirroringActivityRecord() {
} }
} }
// TODO(jrw): Detect and report errors.
void MirroringActivityRecord::CreateMojoBindings( void MirroringActivityRecord::CreateMojoBindings(
mojom::MediaRouter* media_router) { mojom::MediaRouter* media_router) {
DCHECK(mirroring_type_);
// Get a reference to the mirroring service host. // Get a reference to the mirroring service host.
switch (*mirroring_type_) { switch (*mirroring_type_) {
case MirroringType::kDesktop: { case MirroringType::kDesktop: {
......
...@@ -4,22 +4,257 @@ ...@@ -4,22 +4,257 @@
#include "chrome/browser/media/router/providers/cast/mirroring_activity_record.h" #include "chrome/browser/media/router/providers/cast/mirroring_activity_record.h"
#include "testing/gtest/include/gtest/gtest.h" #include "base/test/bind_test_util.h"
#include "base/test/mock_callback.h"
#include "base/test/values_test_util.h"
#include "chrome/browser/media/router/providers/cast/activity_record_test_base.h"
#include "chrome/browser/media/router/providers/cast/test_util.h"
#include "chrome/browser/media/router/test/mock_mojo_media_router.h"
#include "components/cast_channel/cast_test_util.h"
#include "components/mirroring/mojom/session_parameters.mojom.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
using mirroring::mojom::CastMessagePtr; using base::test::IsJson;
using mirroring::mojom::SessionError; using testing::_;
using testing::WithArg;
namespace media_router { namespace media_router {
namespace {
class MirroringActivityRecordTest : public testing::Test { constexpr int kTabId = 123;
constexpr char kDescription[] = "";
constexpr char kDesktopMediaId[] = "theDesktopMediaId";
constexpr char kPresentationId[] = "thePresentationId";
class MockMirroringServiceHost : public mirroring::mojom::MirroringServiceHost {
public: public:
MirroringActivityRecordTest() { MOCK_METHOD4(
// TODO(jrw) Start,
void(mirroring::mojom::SessionParametersPtr params,
mojo::PendingRemote<mirroring::mojom::SessionObserver> observer,
mojo::PendingRemote<mirroring::mojom::CastMessageChannel>
outbound_channel,
mojo::PendingReceiver<mirroring::mojom::CastMessageChannel>
inbound_channel));
};
class MockCastMessageChannel : public mirroring::mojom::CastMessageChannel {
public:
MOCK_METHOD1(Send, void(mirroring::mojom::CastMessagePtr message));
};
} // namespace
class MirroringActivityRecordTest
: public ActivityRecordTestBase,
public testing::WithParamInterface<const char* /*namespace*/> {
protected:
void SetUp() override {
ActivityRecordTestBase::SetUp();
auto make_mirroring_service =
[this](mojo::PendingReceiver<mirroring::mojom::MirroringServiceHost>
receiver) {
ASSERT_FALSE(mirroring_service_);
auto mirroring_service = std::make_unique<MockMirroringServiceHost>();
mirroring_service_ = mirroring_service.get();
mojo::MakeSelfOwnedReceiver(std::move(mirroring_service),
std::move(receiver));
};
ON_CALL(media_router_, GetMirroringServiceHostForDesktop)
.WillByDefault(WithArg<2>(make_mirroring_service));
ON_CALL(media_router_, GetMirroringServiceHostForTab)
.WillByDefault(WithArg<1>(make_mirroring_service));
ON_CALL(media_router_, GetMirroringServiceHostForOffscreenTab)
.WillByDefault(WithArg<2>(make_mirroring_service));
} }
~MirroringActivityRecordTest() override { void MakeRecord() { MakeRecord(MediaSource::ForTab(kTabId), kTabId); }
// TODO(jrw)
void MakeRecord(const MediaSource& source, int tab_id = -1) {
CastSinkExtraData cast_data;
cast_data.cast_channel_id = kChannelId;
cast_data.capabilities = cast_channel::AUDIO_OUT | cast_channel::VIDEO_OUT;
MediaRoute route(kRouteId, source, kSinkId, kDescription, route_is_local_,
true);
route.set_presentation_id(kPresentationId);
record_ = std::make_unique<MirroringActivityRecord>(
route, kAppId, &message_handler_, &session_tracker_, kTabId, cast_data,
on_stop_.Get());
if (route_is_local_) {
record_->CreateMojoBindings(&media_router_);
EXPECT_CALL(*mirroring_service_, Start)
.WillOnce(WithArg<3>(
[this](mojo::PendingReceiver<mirroring::mojom::CastMessageChannel>
inbound_channel) {
ASSERT_FALSE(channel_to_service_);
auto channel = std::make_unique<MockCastMessageChannel>();
channel_to_service_ = channel.get();
mojo::MakeSelfOwnedReceiver(std::move(channel),
std::move(inbound_channel));
}));
}
record_->SetOrUpdateSession(*session_, sink_, kHashToken);
RunUntilIdle();
} }
bool route_is_local_ = true;
MockCastMessageChannel* channel_to_service_ = nullptr;
MockMirroringServiceHost* mirroring_service_ = nullptr;
MockMojoMediaRouter media_router_;
base::MockCallback<MirroringActivityRecord::OnStopCallback> on_stop_;
std::unique_ptr<MirroringActivityRecord> record_;
}; };
INSTANTIATE_TEST_CASE_P(Namespaces,
MirroringActivityRecordTest,
testing::Values(mirroring::mojom::kWebRtcNamespace,
mirroring::mojom::kRemotingNamespace));
TEST_F(MirroringActivityRecordTest, CreateMojoBindingsForDesktop) {
EXPECT_CALL(media_router_,
GetMirroringServiceHostForDesktop(_, kDesktopMediaId, _));
MediaSource source = MediaSource::ForDesktop(kDesktopMediaId);
ASSERT_TRUE(source.IsDesktopMirroringSource());
MakeRecord(source);
}
TEST_F(MirroringActivityRecordTest, CreateMojoBindingsForTab) {
EXPECT_CALL(media_router_, GetMirroringServiceHostForTab(kTabId, _));
MediaSource source = MediaSource::ForTab(kTabId);
ASSERT_TRUE(source.IsTabMirroringSource());
MakeRecord(source, kTabId);
}
TEST_F(MirroringActivityRecordTest, CreateMojoBindingsForTabWithCastAppUrl) {
GURL url(kMirroringAppUri);
EXPECT_CALL(media_router_, GetMirroringServiceHostForTab(kTabId, _));
MediaSource source = MediaSource::ForPresentationUrl(url);
ASSERT_TRUE(source.IsCastPresentationUrl());
MakeRecord(source, kTabId);
}
TEST_F(MirroringActivityRecordTest, CreateMojoBindingsForOffscreenTab) {
static constexpr char kUrl[] = "http://wikipedia.org";
GURL url(kUrl);
EXPECT_CALL(media_router_,
GetMirroringServiceHostForOffscreenTab(url, kPresentationId, _));
MediaSource source = MediaSource::ForPresentationUrl(url);
ASSERT_FALSE(source.IsCastPresentationUrl());
MakeRecord(source);
}
TEST_F(MirroringActivityRecordTest, OnError) {
MakeRecord();
EXPECT_CALL(on_stop_, Run());
record_->OnError(mirroring::mojom::SessionError::CAST_TRANSPORT_ERROR);
RunUntilIdle();
}
TEST_F(MirroringActivityRecordTest, DidStop) {
MakeRecord();
EXPECT_CALL(on_stop_, Run());
record_->DidStop();
RunUntilIdle();
}
TEST_F(MirroringActivityRecordTest, SendWebRtc) {
MakeRecord();
static constexpr char kPayload[] = R"({"foo": "bar"})";
EXPECT_CALL(message_handler_, SendCastMessage(kChannelId, _))
.WillOnce(
WithArg<1>([this](const cast::channel::CastMessage& cast_message) {
EXPECT_EQ(message_handler_.sender_id(), cast_message.source_id());
EXPECT_EQ("theTransportId", cast_message.destination_id());
EXPECT_EQ(mirroring::mojom::kWebRtcNamespace,
cast_message.namespace_());
EXPECT_TRUE(cast_message.has_payload_utf8());
EXPECT_THAT(cast_message.payload_utf8(), IsJson(kPayload));
EXPECT_FALSE(cast_message.has_payload_binary());
return cast_channel::Result::kOk;
}));
record_->Send(mirroring::mojom::CastMessage::New("the_namespace", kPayload));
RunUntilIdle();
}
TEST_F(MirroringActivityRecordTest, SendRemoting) {
MakeRecord();
static constexpr char kPayload[] = R"({"type": "RPC"})";
EXPECT_CALL(message_handler_, SendCastMessage(kChannelId, _))
.WillOnce(WithArg<1>([](const cast::channel::CastMessage& cast_message) {
EXPECT_EQ(mirroring::mojom::kRemotingNamespace,
cast_message.namespace_());
return cast_channel::Result::kOk;
}));
record_->Send(mirroring::mojom::CastMessage::New("the_namespace", kPayload));
RunUntilIdle();
}
TEST_F(MirroringActivityRecordTest, OnAppMessageWrongNamespace) {
MakeRecord();
EXPECT_CALL(*channel_to_service_, Send).Times(0);
cast::channel::CastMessage message;
message.set_namespace_("wrong_namespace");
record_->OnAppMessage(message);
}
TEST_P(MirroringActivityRecordTest, OnAppMessageWrongNonlocal) {
route_is_local_ = false;
MakeRecord();
ASSERT_FALSE(channel_to_service_);
cast::channel::CastMessage message;
message.set_namespace_(GetParam());
record_->OnAppMessage(message);
}
TEST_P(MirroringActivityRecordTest, OnAppMessage) {
MakeRecord();
static constexpr char kPayload[] = R"({"foo": "bar"})";
EXPECT_CALL(*channel_to_service_, Send)
.WillOnce([](mirroring::mojom::CastMessagePtr message) {
EXPECT_EQ(GetParam(), message->message_namespace);
EXPECT_EQ(kPayload, message->json_format_data);
});
cast::channel::CastMessage message;
message.set_namespace_(GetParam());
message.set_protocol_version(
cast::channel::CastMessage_ProtocolVersion_CASTV2_1_0);
message.set_payload_utf8(kPayload);
record_->OnAppMessage(message);
}
TEST_F(MirroringActivityRecordTest, OnInternalMessageNonlocal) {
route_is_local_ = false;
MakeRecord();
ASSERT_FALSE(channel_to_service_);
record_->OnInternalMessage(cast_channel::InternalMessage(
cast_channel::CastMessageType::kPing, "the_namespace", base::Value()));
}
TEST_F(MirroringActivityRecordTest, OnInternalMessage) {
MakeRecord();
static constexpr char kPayload[] = R"({"foo": "bar"})";
static constexpr char kNamespace[] = "the_namespace";
EXPECT_CALL(*channel_to_service_, Send)
.WillOnce([](mirroring::mojom::CastMessagePtr message) {
EXPECT_EQ(kNamespace, message->message_namespace);
EXPECT_THAT(message->json_format_data, IsJson(kPayload));
});
record_->OnInternalMessage(cast_channel::InternalMessage(
cast_channel::CastMessageType::kPing, kNamespace,
base::test::ParseJson(kPayload)));
}
} // namespace media_router } // namespace media_router
...@@ -192,6 +192,8 @@ class MockCastMessageHandler : public CastMessageHandler { ...@@ -192,6 +192,8 @@ class MockCastMessageHandler : public CastMessageHandler {
ResultCallback callback)); ResultCallback callback));
MOCK_METHOD2(SendAppMessage, MOCK_METHOD2(SendAppMessage,
Result(int channel_id, const CastMessage& message)); Result(int channel_id, const CastMessage& message));
MOCK_METHOD2(SendCastMessage,
Result(int channel_id, const CastMessage& message));
MOCK_METHOD4(SendMediaRequest, MOCK_METHOD4(SendMediaRequest,
base::Optional<int>(int channel_id, base::Optional<int>(int channel_id,
const base::Value& body, const base::Value& body,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment