blob: 7d98b3a957d4f102c17d09835b49a95ef2ed6159 [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 "services/media_session/media_controller.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "services/media_session/media_session_service.h"
#include "services/media_session/public/cpp/media_metadata.h"
#include "services/media_session/public/cpp/test/mock_media_session.h"
#include "services/media_session/public/cpp/test/test_media_controller.h"
#include "services/media_session/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/test/test_connector_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media_session {
class MediaControllerTest : public testing::Test {
public:
MediaControllerTest() = default;
void SetUp() override {
// Create an instance of the MediaSessionService and bind some interfaces.
service_ = std::make_unique<MediaSessionService>(
connector_factory_.RegisterInstance(mojom::kServiceName));
connector_factory_.GetDefaultConnector()->BindInterface(mojom::kServiceName,
&audio_focus_ptr_);
connector_factory_.GetDefaultConnector()->BindInterface(
mojom::kServiceName, &controller_manager_ptr_);
controller_manager_ptr_->CreateActiveMediaController(
mojo::MakeRequest(&media_controller_ptr_));
controller_manager_ptr_.FlushForTesting();
audio_focus_ptr_->SetEnforcementMode(
mojom::EnforcementMode::kSingleSession);
audio_focus_ptr_.FlushForTesting();
}
void TearDown() override {
// Run pending tasks.
base::RunLoop().RunUntilIdle();
}
void RequestAudioFocus(test::MockMediaSession& session,
mojom::AudioFocusType type) {
session.RequestAudioFocusFromService(audio_focus_ptr_, type);
}
mojom::MediaControllerPtr& controller() { return media_controller_ptr_; }
mojom::MediaControllerManagerPtr& manager() {
return controller_manager_ptr_;
}
static size_t GetImageObserverCount(const MediaController& controller) {
return controller.image_observers_.size();
}
private:
base::test::ScopedTaskEnvironment task_environment_;
service_manager::TestConnectorFactory connector_factory_;
std::unique_ptr<MediaSessionService> service_;
mojom::AudioFocusManagerPtr audio_focus_ptr_;
mojom::MediaControllerPtr media_controller_ptr_;
mojom::MediaControllerManagerPtr controller_manager_ptr_;
DISALLOW_COPY_AND_ASSIGN(MediaControllerTest);
};
TEST_F(MediaControllerTest, ActiveController_Suspend) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
controller()->Suspend();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
}
}
TEST_F(MediaControllerTest, ActiveController_Multiple_Abandon_Top) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
media_session_1.SetIsControllable(true);
media_session_2.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer_1(media_session_1);
test::MockMediaSessionMojoObserver observer_2(media_session_2);
RequestAudioFocus(media_session_2, mojom::AudioFocusType::kGain);
observer_1.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
observer_2.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session_2.AbandonAudioFocusFromClient();
{
test::MockMediaSessionMojoObserver observer(media_session_1);
controller()->Resume();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
}
TEST_F(MediaControllerTest,
ActiveController_Multiple_Abandon_UnderNonControllable) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
media_session_1.SetIsControllable(true);
media_session_2.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer_1(media_session_1);
test::MockMediaSessionMojoObserver observer_2(media_session_2);
RequestAudioFocus(media_session_2, mojom::AudioFocusType::kGain);
observer_1.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
observer_2.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer_2(media_session_2);
test::MockMediaSessionMojoObserver observer_3(media_session_3);
RequestAudioFocus(media_session_3, mojom::AudioFocusType::kGain);
observer_2.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
observer_3.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
media_session_2.AbandonAudioFocusFromClient();
{
test::MockMediaSessionMojoObserver observer(media_session_1);
controller()->Resume();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
}
TEST_F(MediaControllerTest, ActiveController_Multiple_Controllable) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
media_session_1.SetIsControllable(true);
media_session_2.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer_1(media_session_1);
test::MockMediaSessionMojoObserver observer_2(media_session_2);
RequestAudioFocus(media_session_2, mojom::AudioFocusType::kGain);
observer_1.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
observer_2.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer(media_session_2);
controller()->Suspend();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
}
}
TEST_F(MediaControllerTest, ActiveController_Multiple_NonControllable) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
media_session_1.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
EXPECT_EQ(2, media_session_1.add_observer_count());
{
test::MockMediaSessionMojoObserver observer_1(media_session_1);
test::MockMediaSessionMojoObserver observer_2(media_session_2);
RequestAudioFocus(media_session_2, mojom::AudioFocusType::kGainTransient);
observer_1.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
observer_2.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
// The top session has changed but the controller is still bound to
// |media_session_1|. We should make sure we do not add an observer if we
// already have one.
EXPECT_EQ(3, media_session_1.add_observer_count());
{
test::MockMediaSessionMojoObserver observer(media_session_1);
controller()->Resume();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
EXPECT_EQ(4, media_session_1.add_observer_count());
}
TEST_F(MediaControllerTest, ActiveController_Multiple_UpdateControllable) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
media_session_1.SetIsControllable(true);
media_session_2.SetIsControllable(true);
EXPECT_EQ(0, media_session_1.add_observer_count());
EXPECT_EQ(0, media_session_2.add_observer_count());
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
EXPECT_EQ(2, media_session_1.add_observer_count());
EXPECT_EQ(0, media_session_2.add_observer_count());
{
test::MockMediaSessionMojoObserver observer(media_session_2);
RequestAudioFocus(media_session_2, mojom::AudioFocusType::kGainTransient);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
EXPECT_EQ(2, media_session_1.add_observer_count());
EXPECT_EQ(2, media_session_2.add_observer_count());
media_session_2.SetIsControllable(false);
media_session_2.FlushForTesting();
EXPECT_EQ(3, media_session_1.add_observer_count());
EXPECT_EQ(2, media_session_2.add_observer_count());
media_session_1.SetIsControllable(false);
media_session_1.FlushForTesting();
EXPECT_EQ(3, media_session_1.add_observer_count());
EXPECT_EQ(2, media_session_2.add_observer_count());
}
TEST_F(MediaControllerTest, ActiveController_Suspend_Noop) {
controller()->Suspend();
}
TEST_F(MediaControllerTest, ActiveController_Suspend_Noop_Abandoned) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
media_session.AbandonAudioFocusFromClient();
controller()->Suspend();
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
}
TEST_F(MediaControllerTest, ActiveController_SuspendResume) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
controller()->Suspend();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
controller()->Resume();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
}
TEST_F(MediaControllerTest, ActiveController_ToggleSuspendResume_Playing) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
controller()->ToggleSuspendResume();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
}
}
TEST_F(MediaControllerTest, ActiveController_ToggleSuspendResume_Ducked) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
media_session.StartDucking();
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kDucking);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
controller()->ToggleSuspendResume();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
}
}
TEST_F(MediaControllerTest, ActiveController_ToggleSuspendResume_Inactive) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
media_session.Stop(mojom::MediaSession::SuspendType::kUI);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kInactive);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
controller()->ToggleSuspendResume();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
}
TEST_F(MediaControllerTest, ActiveController_ToggleSuspendResume_Paused) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
controller()->Suspend();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
controller()->ToggleSuspendResume();
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
}
TEST_F(MediaControllerTest, ActiveController_Observer_StateTransition) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
media_session_1.SetIsControllable(true);
media_session_2.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
controller()->Suspend();
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
}
{
test::TestMediaControllerObserver observer(controller());
RequestAudioFocus(media_session_2, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::MockMediaSessionMojoObserver observer(media_session_1);
media_session_1.StartDucking();
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kDucking);
}
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
}
TEST_F(MediaControllerTest, ActiveController_PreviousTrack) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
EXPECT_EQ(0, media_session.prev_track_count());
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
EXPECT_EQ(0, media_session.prev_track_count());
}
controller()->PreviousTrack();
controller().FlushForTesting();
EXPECT_EQ(1, media_session.prev_track_count());
}
TEST_F(MediaControllerTest, ActiveController_NextTrack) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
EXPECT_EQ(0, media_session.next_track_count());
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
EXPECT_EQ(0, media_session.next_track_count());
}
controller()->NextTrack();
controller().FlushForTesting();
EXPECT_EQ(1, media_session.next_track_count());
}
TEST_F(MediaControllerTest, ActiveController_Seek) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
EXPECT_EQ(0, media_session.seek_count());
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
EXPECT_EQ(0, media_session.seek_count());
}
controller()->Seek(
base::TimeDelta::FromSeconds(mojom::kDefaultSeekTimeSeconds));
controller().FlushForTesting();
EXPECT_EQ(1, media_session.seek_count());
}
TEST_F(MediaControllerTest, ActiveController_SeekTo) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
EXPECT_EQ(0, media_session.seek_to_count());
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
EXPECT_EQ(0, media_session.seek_to_count());
}
controller()->SeekTo(
base::TimeDelta::FromSeconds(mojom::kDefaultSeekTimeSeconds));
controller().FlushForTesting();
EXPECT_EQ(1, media_session.seek_to_count());
}
TEST_F(MediaControllerTest, ActiveController_ScrubTo) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
EXPECT_FALSE(media_session.is_scrubbing());
EXPECT_EQ(0, media_session.seek_to_count());
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
EXPECT_FALSE(media_session.is_scrubbing());
EXPECT_EQ(0, media_session.seek_to_count());
}
controller()->ScrubTo(
base::TimeDelta::FromSeconds(mojom::kDefaultSeekTimeSeconds));
controller().FlushForTesting();
EXPECT_TRUE(media_session.is_scrubbing());
EXPECT_EQ(0, media_session.seek_to_count());
controller()->ScrubTo(
base::TimeDelta::FromSeconds(mojom::kDefaultSeekTimeSeconds));
controller().FlushForTesting();
EXPECT_TRUE(media_session.is_scrubbing());
EXPECT_EQ(0, media_session.seek_to_count());
controller()->SeekTo(
base::TimeDelta::FromSeconds(mojom::kDefaultSeekTimeSeconds));
controller().FlushForTesting();
EXPECT_FALSE(media_session.is_scrubbing());
EXPECT_EQ(1, media_session.seek_to_count());
}
TEST_F(MediaControllerTest, ActiveController_Metadata_Observer_Abandoned) {
MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
metadata.album = base::ASCIIToUTF16("album");
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
base::Optional<MediaMetadata> test_metadata(metadata);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session.SimulateMetadataChanged(test_metadata);
media_session.AbandonAudioFocusFromClient();
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForEmptyMetadata();
}
}
TEST_F(MediaControllerTest, ActiveController_Metadata_Observer_Empty) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
base::Optional<MediaMetadata> test_metadata;
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
media_session.SimulateMetadataChanged(test_metadata);
observer.WaitForEmptyMetadata();
}
}
TEST_F(MediaControllerTest, ActiveController_Metadata_Observer_WithInfo) {
MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
metadata.album = base::ASCIIToUTF16("album");
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
base::Optional<MediaMetadata> test_metadata(metadata);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
media_session.SimulateMetadataChanged(test_metadata);
observer.WaitForExpectedMetadata(metadata);
}
}
TEST_F(MediaControllerTest, ActiveController_Metadata_AddObserver_Empty) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
base::Optional<MediaMetadata> test_metadata;
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session.SimulateMetadataChanged(test_metadata);
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForEmptyMetadata();
}
}
TEST_F(MediaControllerTest, ActiveController_Metadata_AddObserver_WithInfo) {
MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
metadata.album = base::ASCIIToUTF16("album");
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
base::Optional<MediaMetadata> test_metadata(metadata);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session.SimulateMetadataChanged(test_metadata);
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForExpectedMetadata(metadata);
}
}
TEST_F(MediaControllerTest, ActiveController_Stop) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::MockMediaSessionMojoObserver observer(media_session);
controller()->Stop();
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kInactive);
}
}
TEST_F(MediaControllerTest, BoundController_Routing) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
media_session_1.SetIsControllable(true);
media_session_2.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
mojom::MediaControllerPtr controller;
manager()->CreateMediaControllerForSession(mojo::MakeRequest(&controller),
media_session_1.request_id());
manager().FlushForTesting();
EXPECT_EQ(0, media_session_1.next_track_count());
controller->NextTrack();
controller.FlushForTesting();
EXPECT_EQ(1, media_session_1.next_track_count());
{
test::MockMediaSessionMojoObserver observer(media_session_2);
RequestAudioFocus(media_session_2, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
EXPECT_EQ(1, media_session_1.next_track_count());
EXPECT_EQ(0, media_session_2.next_track_count());
controller->NextTrack();
controller.FlushForTesting();
EXPECT_EQ(2, media_session_1.next_track_count());
EXPECT_EQ(0, media_session_2.next_track_count());
}
TEST_F(MediaControllerTest, BoundController_BadRequestId) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
mojom::MediaControllerPtr controller;
manager()->CreateMediaControllerForSession(mojo::MakeRequest(&controller),
base::UnguessableToken::Create());
manager().FlushForTesting();
EXPECT_EQ(0, media_session.next_track_count());
controller->NextTrack();
controller.FlushForTesting();
EXPECT_EQ(0, media_session.next_track_count());
}
TEST_F(MediaControllerTest, BoundController_DropOnAbandon) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
mojom::MediaControllerPtr controller;
manager()->CreateMediaControllerForSession(mojo::MakeRequest(&controller),
media_session.request_id());
manager().FlushForTesting();
EXPECT_EQ(0, media_session.next_track_count());
controller->NextTrack();
controller.FlushForTesting();
EXPECT_EQ(1, media_session.next_track_count());
media_session.AbandonAudioFocusFromClient();
EXPECT_EQ(1, media_session.next_track_count());
controller->NextTrack();
controller.FlushForTesting();
EXPECT_EQ(1, media_session.next_track_count());
}
TEST_F(MediaControllerTest, ActiveController_Actions_AddObserver_Empty) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForEmptyActions();
}
}
TEST_F(MediaControllerTest, ActiveController_Actions_AddObserver_WithInfo) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session.EnableAction(mojom::MediaSessionAction::kPlay);
{
test::TestMediaControllerObserver observer(controller());
std::set<mojom::MediaSessionAction> expected_actions;
expected_actions.insert(mojom::MediaSessionAction::kPlay);
observer.WaitForExpectedActions(expected_actions);
}
}
TEST_F(MediaControllerTest, ActiveController_Actions_Observer_Empty) {
test::MockMediaSession media_session;
media_session.EnableAction(mojom::MediaSessionAction::kPlay);
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
media_session.DisableAction(mojom::MediaSessionAction::kPlay);
observer.WaitForEmptyActions();
}
}
TEST_F(MediaControllerTest, ActiveController_Actions_Observer_WithInfo) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
media_session.EnableAction(mojom::MediaSessionAction::kPlay);
std::set<mojom::MediaSessionAction> expected_actions;
expected_actions.insert(mojom::MediaSessionAction::kPlay);
observer.WaitForExpectedActions(expected_actions);
}
}
TEST_F(MediaControllerTest, ActiveController_Actions_Observer_Abandoned) {
test::MockMediaSession media_session;
media_session.EnableAction(mojom::MediaSessionAction::kPlay);
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session.AbandonAudioFocusFromClient();
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForEmptyActions();
}
}
TEST_F(MediaControllerTest, ActiveController_Observer_Abandoned) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
media_session.AbandonAudioFocusFromClient();
// We should see empty info, metadata and actions flushed since the active
// controller is no longer bound to a media session.
observer.WaitForEmptyInfo();
observer.WaitForEmptyMetadata();
observer.WaitForEmptyActions();
}
}
TEST_F(MediaControllerTest, ActiveController_AddObserver_Abandoned) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
media_session.AbandonAudioFocusFromClient();
{
test::TestMediaControllerObserver observer(controller());
// We should see empty info, metadata and actions since the active
// controller is no longer bound to a media session.
observer.WaitForEmptyInfo();
observer.WaitForEmptyMetadata();
observer.WaitForEmptyActions();
}
}
TEST_F(MediaControllerTest, ClearImageObserverOnError) {
MediaController controller;
mojom::MediaControllerPtr controller_ptr;
controller.BindToInterface(mojo::MakeRequest(&controller_ptr));
EXPECT_EQ(0u, GetImageObserverCount(controller));
{
test::TestMediaControllerImageObserver observer(controller_ptr, 0, 0);
EXPECT_EQ(1u, GetImageObserverCount(controller));
}
EXPECT_EQ(1u, GetImageObserverCount(controller));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, GetImageObserverCount(controller));
}
TEST_F(MediaControllerTest, ActiveController_SimulateImagesChanged) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
std::vector<MediaImage> images;
MediaImage image;
image.src = GURL("https://www.google.com");
images.push_back(image);
{
test::TestMediaControllerImageObserver observer(controller(), 0, 0);
// By default, we should receive an empty image.
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
true);
EXPECT_TRUE(media_session.last_image_src().is_empty());
// Check that we receive the correct image and that it was requested from
// |media_session| by the controller.
media_session.SetImagesOfType(mojom::MediaSessionImageType::kArtwork,
images);
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
false);
EXPECT_EQ(image.src, media_session.last_image_src());
// Check that we flush the observer with an empty image. Since the image is
// empty the last downloaded image by |media_session| should still be the
// previous image.
media_session.SetImagesOfType(mojom::MediaSessionImageType::kArtwork,
std::vector<MediaImage>());
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
true);
EXPECT_EQ(image.src, media_session.last_image_src());
}
}
TEST_F(MediaControllerTest,
ActiveController_SimulateImagesChanged_ToggleControllable) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
std::vector<MediaImage> images;
MediaImage image;
image.src = GURL("https://www.google.com");
images.push_back(image);
media_session.SetImagesOfType(mojom::MediaSessionImageType::kArtwork, images);
{
test::TestMediaControllerImageObserver observer(controller(), 0, 0);
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
false);
EXPECT_EQ(image.src, media_session.last_image_src());
// When the |media_session| becomes uncontrollable it is unbound from the
// media controller and we should flush the observer with an empty image.
media_session.SetIsControllable(false);
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
true);
// When the |media_session| becomes controllable again it will be bound to
// the media controller and we should flush the observer with the current
// images.
media_session.SetIsControllable(true);
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
false);
EXPECT_EQ(image.src, media_session.last_image_src());
}
}
TEST_F(MediaControllerTest,
ActiveController_SimulateImagesChanged_TypeChanged) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
std::vector<MediaImage> images;
MediaImage image;
image.src = GURL("https://www.google.com");
images.push_back(image);
media_session.SetImagesOfType(mojom::MediaSessionImageType::kArtwork, images);
{
test::TestMediaControllerImageObserver observer(controller(), 0, 0);
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
false);
EXPECT_EQ(image.src, media_session.last_image_src());
// If we clear all the images associated with the media session we should
// flush all the observers.
media_session.ClearAllImages();
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
true);
EXPECT_EQ(image.src, media_session.last_image_src());
}
}
TEST_F(MediaControllerTest,
ActiveController_SimulateImagesChanged_MinSizeCutoff) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
std::vector<MediaImage> images;
MediaImage image1;
image1.src = GURL("https://www.google.com");
image1.sizes.push_back(gfx::Size(1, 1));
images.push_back(image1);
media_session.SetImagesOfType(mojom::MediaSessionImageType::kArtwork, images);
{
test::TestMediaControllerImageObserver observer(controller(), 5, 10);
// The observer requires an image that is at least 5px but the only image
// we have is 1px so the observer will receive a null image.
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
true);
EXPECT_TRUE(media_session.last_image_src().is_empty());
MediaImage image2;
image2.src = GURL("https://www.example.com");
image2.sizes.push_back(gfx::Size(10, 10));
images.push_back(image2);
// Update the media session with two images, one that is too small and one
// that is the right size. We should receive the second image through the
// observer.
media_session.SetImagesOfType(mojom::MediaSessionImageType::kArtwork,
images);
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
false);
EXPECT_EQ(image2.src, media_session.last_image_src());
}
}
TEST_F(MediaControllerTest,
ActiveController_SimulateImagesChanged_DesiredSize) {
test::MockMediaSession media_session;
media_session.SetIsControllable(true);
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
std::vector<MediaImage> images;
MediaImage image1;
image1.src = GURL("https://www.google.com");
image1.sizes.push_back(gfx::Size(10, 10));
images.push_back(image1);
MediaImage image2;
image2.src = GURL("https://www.example.com");
image2.sizes.push_back(gfx::Size(9, 9));
images.push_back(image2);
media_session.SetImagesOfType(mojom::MediaSessionImageType::kArtwork, images);
{
test::TestMediaControllerImageObserver observer(controller(), 5, 10);
// The media session has two images, but the first one is closer to the 10px
// desired size that the observer has specified. Therefore, the observer
// should receive that image.
media_session.SetImagesOfType(mojom::MediaSessionImageType::kArtwork,
images);
observer.WaitForExpectedImageOfType(mojom::MediaSessionImageType::kArtwork,
false);
EXPECT_EQ(image1.src, media_session.last_image_src());
}
}
TEST_F(MediaControllerTest, ActiveController_Observer_SessionChanged) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
media_session_1.SetIsControllable(true);
media_session_2.SetIsControllable(true);
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForSession(base::nullopt);
}
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForSession(media_session_1.request_id());
}
{
test::TestMediaControllerObserver observer(controller());
RequestAudioFocus(media_session_2, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::TestMediaControllerObserver observer(controller());
observer.WaitForSession(media_session_2.request_id());
}
{
test::TestMediaControllerObserver observer(controller());
media_session_2.AbandonAudioFocusFromClient();
observer.WaitForSession(media_session_1.request_id());
}
{
test::TestMediaControllerObserver observer(controller());
media_session_1.SetIsControllable(false);
observer.WaitForSession(base::nullopt);
}
}
TEST_F(MediaControllerTest, BoundController_Observer_SessionChanged) {
test::MockMediaSession media_session;
{
test::MockMediaSessionMojoObserver observer(media_session);
RequestAudioFocus(media_session, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
mojom::MediaControllerPtr controller;
manager()->CreateMediaControllerForSession(mojo::MakeRequest(&controller),
media_session.request_id());
manager().FlushForTesting();
{
test::TestMediaControllerObserver observer(controller);
observer.WaitForSession(media_session.request_id());
}
}
TEST_F(MediaControllerTest, Manager_SuspendAllSessions) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
{
test::MockMediaSessionMojoObserver observer(media_session_2);
RequestAudioFocus(media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
}
manager()->SuspendAllSessions();
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
}
{
test::MockMediaSessionMojoObserver observer(media_session_2);
observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
}
}
} // namespace media_session