| Index: content/browser/renderer_host/media/render_frame_audio_output_stream_factory_unittest.cc
|
| diff --git a/content/browser/renderer_host/media/render_frame_audio_output_stream_factory_unittest.cc b/content/browser/renderer_host/media/render_frame_audio_output_stream_factory_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..dee76706f8d7e357fc4bf4dbfc3c09d72eac8bd7
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/media/render_frame_audio_output_stream_factory_unittest.cc
|
| @@ -0,0 +1,295 @@
|
| +// Copyright 2016 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/renderer_host/media/render_frame_audio_output_stream_factory.h"
|
| +
|
| +#include <cmath>
|
| +#include <limits>
|
| +#include <utility>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/memory/shared_memory.h"
|
| +#include "base/memory/shared_memory_handle.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/sync_socket.h"
|
| +#include "content/browser/audio_manager_thread.h"
|
| +#include "content/browser/renderer_host/media/audio_output_stream_factory_context.h"
|
| +#include "content/browser/renderer_host/media/media_stream_manager.h"
|
| +#include "content/common/media/audio_output_stream_factory.mojom.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "content/public/test/mock_render_process_host.h"
|
| +#include "content/public/test/test_browser_context.h"
|
| +#include "content/public/test/test_browser_thread_bundle.h"
|
| +#include "media/audio/audio_output_controller.h"
|
| +#include "media/audio/fake_audio_log_factory.h"
|
| +#include "media/audio/fake_audio_manager.h"
|
| +#include "media/base/audio_parameters.h"
|
| +#include "media/base/media_switches.h"
|
| +#include "mojo/public/cpp/bindings/binding.h"
|
| +#include "mojo/public/cpp/system/platform_handle.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +using testing::Test;
|
| +using AudioOutputStreamFactory = mojom::RendererAudioOutputStreamFactory;
|
| +using AudioOutputStreamFactoryPtr =
|
| + mojo::InterfacePtr<AudioOutputStreamFactory>;
|
| +using AudioOutputStreamFactoryRequest =
|
| + mojo::InterfaceRequest<AudioOutputStreamFactory>;
|
| +using AudioOutputStream = media::mojom::AudioOutputStream;
|
| +using AudioOutputStreamPtr = mojo::InterfacePtr<AudioOutputStream>;
|
| +using AudioOutputStreamRequest = mojo::InterfaceRequest<AudioOutputStream>;
|
| +using AudioOutputStreamProvider = media::mojom::AudioOutputStreamProvider;
|
| +using AudioOutputStreamProviderPtr =
|
| + mojo::InterfacePtr<AudioOutputStreamProvider>;
|
| +using AudioOutputStreamProviderRequest =
|
| + mojo::InterfaceRequest<AudioOutputStreamProvider>;
|
| +
|
| +const int kStreamId = 0;
|
| +const int kNoSessionId = 0;
|
| +const int kRenderProcessId = 42;
|
| +const int kRenderFrameId = 24;
|
| +const char kSecurityOrigin[] = "http://localhost";
|
| +const char kSalt[] = "salt";
|
| +const media::AudioParameters params(
|
| + media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
|
| + media::CHANNEL_LAYOUT_MONO,
|
| + 44100 /*sample frequency*/,
|
| + 16 /*bits per sample*/,
|
| + 441 /*10 ms buffers*/);
|
| +
|
| +class MockAudioOutputDelegate : public media::AudioOutputDelegate {
|
| + MOCK_CONST_METHOD0(GetController,
|
| + scoped_refptr<media::AudioOutputController>());
|
| + MOCK_CONST_METHOD0(GetStreamId, int());
|
| + MOCK_METHOD0(OnPlayStream, void());
|
| + MOCK_METHOD0(OnPauseStream, void());
|
| + MOCK_METHOD1(OnSetVolume, void(double));
|
| +};
|
| +
|
| +class MockContext : public AudioOutputStreamFactoryContext {
|
| + public:
|
| + explicit MockContext(bool auth_ok, int render_process_id = kRenderProcessId)
|
| + : render_process_id_(render_process_id),
|
| + salt_(kSalt),
|
| + auth_ok_(auth_ok) {}
|
| +
|
| + ~MockContext() override { EXPECT_EQ(nullptr, delegate_); }
|
| +
|
| + int GetRenderProcessId() const override { return render_process_id_; }
|
| +
|
| + void RequestDeviceAuthorization(
|
| + int render_frame_id,
|
| + int session_id,
|
| + const std::string& device_id,
|
| + const url::Origin& security_origin,
|
| + AuthorizationCompletedCallback cb) const override {
|
| + EXPECT_EQ(render_frame_id, kRenderFrameId);
|
| + EXPECT_EQ(session_id, 0);
|
| + if (auth_ok_) {
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(cb, media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK,
|
| + false, media::AudioParameters::UnavailableDeviceParams(),
|
| + "default"));
|
| + return;
|
| + }
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(cb,
|
| + media::OutputDeviceStatus::
|
| + OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
|
| + false, media::AudioParameters::UnavailableDeviceParams(),
|
| + "default"));
|
| + }
|
| +
|
| + const std::string& GetSalt() const override { return salt_; }
|
| +
|
| + // The event handler for the delegate will be stored at
|
| + // |*event_handler_location| when the delegate is created.
|
| + void PrepareDelegateForCreation(
|
| + std::unique_ptr<media::AudioOutputDelegate> delegate,
|
| + media::AudioOutputDelegate::EventHandler** event_handler_location) {
|
| + EXPECT_EQ(nullptr, delegate_);
|
| + EXPECT_EQ(nullptr, delegate_event_handler_location_);
|
| + delegate_ = std::move(delegate);
|
| + delegate_event_handler_location_ = event_handler_location;
|
| + }
|
| +
|
| + std::unique_ptr<media::AudioOutputDelegate> CreateDelegate(
|
| + const std::string& unique_device_id,
|
| + int render_frame_id,
|
| + const media::AudioParameters& params,
|
| + media::AudioOutputDelegate::EventHandler* handler) override {
|
| + EXPECT_NE(nullptr, delegate_);
|
| + EXPECT_NE(nullptr, delegate_event_handler_location_);
|
| + *delegate_event_handler_location_ = handler;
|
| + delegate_event_handler_location_ = nullptr;
|
| + return std::move(delegate_);
|
| + }
|
| +
|
| + MOCK_METHOD1(OnFactoryFinished, void(AudioOutputStreamFactory* factory));
|
| +
|
| + private:
|
| + int render_process_id_ = kRenderProcessId;
|
| + const std::string salt_;
|
| + const bool auth_ok_;
|
| + std::unique_ptr<media::AudioOutputDelegate> delegate_;
|
| + media::AudioOutputDelegate::EventHandler** delegate_event_handler_location_ =
|
| + nullptr;
|
| +};
|
| +
|
| +class MockClient {
|
| + public:
|
| + MockClient() {}
|
| + ~MockClient() {}
|
| +
|
| + void StreamCreated(mojo::ScopedSharedBufferHandle handle1,
|
| + mojo::ScopedHandle handle2) {
|
| + was_called_ = true;
|
| + }
|
| +
|
| + bool was_called() { return was_called_; }
|
| +
|
| + private:
|
| + bool was_called_ = false;
|
| +};
|
| +
|
| +void AuthCallback(media::OutputDeviceStatus* status_out,
|
| + media::AudioParameters* params_out,
|
| + std::string* id_out,
|
| + media::OutputDeviceStatus status,
|
| + const media::AudioParameters& params,
|
| + const std::string& id) {
|
| + *status_out = status;
|
| + *params_out = params;
|
| + *id_out = id;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +class RenderFrameAudioOutputStreamFactoryTest : public Test {
|
| + public:
|
| + RenderFrameAudioOutputStreamFactoryTest() {}
|
| + ~RenderFrameAudioOutputStreamFactoryTest() override {}
|
| +
|
| + private:
|
| + TestBrowserThreadBundle thread_bundle_;
|
| +};
|
| +
|
| +TEST_F(RenderFrameAudioOutputStreamFactoryTest, AuthWithoutStreamRequest) {
|
| + AudioOutputStreamFactoryPtr factory_ptr;
|
| + auto factory_context_ = base::MakeUnique<MockContext>(true);
|
| + auto factory_ = base::MakeUnique<RenderFrameAudioOutputStreamFactory>(
|
| + factory_context_.get(), kRenderFrameId, mojo::MakeRequest(&factory_ptr));
|
| +
|
| + media::OutputDeviceStatus status;
|
| + media::AudioParameters params2;
|
| + std::string id;
|
| + factory_ptr->RequestDeviceAuthorization(
|
| + nullptr, kNoSessionId, "default", url::Origin(GURL(kSecurityOrigin)),
|
| + base::Bind(&AuthCallback, base::Unretained(&status),
|
| + base::Unretained(¶ms2), base::Unretained(&id)));
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_EQ(status, media::OUTPUT_DEVICE_STATUS_OK);
|
| + EXPECT_TRUE(id.empty());
|
| +}
|
| +
|
| +TEST_F(RenderFrameAudioOutputStreamFactoryTest, AuthWithStreamRequest) {
|
| + AudioOutputStreamFactoryPtr factory_ptr;
|
| + AudioOutputStreamProviderPtr provider;
|
| + AudioOutputStreamPtr output_stream;
|
| + MockClient client;
|
| + media::AudioOutputDelegate::EventHandler* event_handler = nullptr;
|
| + auto factory_context = base::MakeUnique<MockContext>(true);
|
| + factory_context->PrepareDelegateForCreation(
|
| + base::MakeUnique<MockAudioOutputDelegate>(), &event_handler);
|
| + auto factory = base::MakeUnique<RenderFrameAudioOutputStreamFactory>(
|
| + factory_context.get(), kRenderFrameId, mojo::MakeRequest(&factory_ptr));
|
| +
|
| + media::OutputDeviceStatus status;
|
| + media::AudioParameters params2;
|
| + std::string id;
|
| + factory_ptr->RequestDeviceAuthorization(
|
| + mojo::MakeRequest(&provider), kNoSessionId, "default",
|
| + url::Origin(GURL(kSecurityOrigin)),
|
| + base::Bind(&AuthCallback, base::Unretained(&status),
|
| + base::Unretained(¶ms2), base::Unretained(&id)));
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_EQ(status, media::OUTPUT_DEVICE_STATUS_OK);
|
| + EXPECT_TRUE(id.empty());
|
| +
|
| + provider->Acquire(
|
| + mojo::MakeRequest<AudioOutputStream>(&output_stream), params,
|
| + base::Bind(&MockClient::StreamCreated, base::Unretained(&client)));
|
| + base::RunLoop().RunUntilIdle();
|
| + ASSERT_NE(event_handler, nullptr);
|
| +
|
| + base::SharedMemory shared_memory;
|
| + ASSERT_TRUE(shared_memory.CreateAndMapAnonymous(100));
|
| +
|
| + base::CancelableSyncSocket local, remote;
|
| + ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&local, &remote));
|
| + event_handler->OnStreamCreated(kStreamId, &shared_memory, &remote);
|
| +
|
| + base::RunLoop().RunUntilIdle();
|
| + // Make sure we got callback from creating stream.
|
| + EXPECT_TRUE(client.was_called());
|
| +}
|
| +
|
| +TEST_F(RenderFrameAudioOutputStreamFactoryTest, AuthWithStreamRequestDenied) {
|
| + AudioOutputStreamFactoryPtr factory_ptr;
|
| + AudioOutputStreamProviderPtr output_provider;
|
| + auto factory_context = base::MakeUnique<MockContext>(false);
|
| + auto factory = base::MakeUnique<RenderFrameAudioOutputStreamFactory>(
|
| + factory_context.get(), kRenderFrameId, mojo::MakeRequest(&factory_ptr));
|
| +
|
| + media::OutputDeviceStatus status;
|
| + media::AudioParameters params2;
|
| + std::string id;
|
| + factory_ptr->RequestDeviceAuthorization(
|
| + mojo::MakeRequest(&output_provider), kNoSessionId, "default",
|
| + url::Origin(GURL(kSecurityOrigin)),
|
| + base::Bind(&AuthCallback, base::Unretained(&status),
|
| + base::Unretained(¶ms2), base::Unretained(&id)));
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_EQ(status, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED);
|
| + EXPECT_TRUE(id.empty());
|
| +}
|
| +
|
| +TEST_F(RenderFrameAudioOutputStreamFactoryTest,
|
| + OutOfRangeSessionId_BadMessage) {
|
| + // This test checks that we get a bad message if session_id is too large
|
| + // to fit in an integer. This ensures that we don't overflow when casting the
|
| + // int64_t to an int
|
| + if (sizeof(int) >= sizeof(int64_t)) {
|
| + // In this case, any int64_t would fit in an int, and the case we are
|
| + // checking for is impossible.
|
| + return;
|
| + }
|
| +
|
| + TestBrowserContext browser_context;
|
| + MockRenderProcessHost rph(&browser_context);
|
| +
|
| + AudioOutputStreamFactoryPtr factory_ptr;
|
| + auto factory_context = base::MakeUnique<MockContext>(true, rph.GetID());
|
| + auto factory = base::MakeUnique<RenderFrameAudioOutputStreamFactory>(
|
| + factory_context.get(), kRenderFrameId, mojo::MakeRequest(&factory_ptr));
|
| + EXPECT_CALL(*factory_context, OnFactoryFinished(factory.get()));
|
| +
|
| + int64_t session_id = std::numeric_limits<int>::max();
|
| + ++session_id;
|
| + factory_ptr->RequestDeviceAuthorization(
|
| + nullptr, session_id, "default", url::Origin(GURL(kSecurityOrigin)),
|
| + base::Bind([](media::OutputDeviceStatus, const media::AudioParameters&,
|
| + const std::string&) { EXPECT_FALSE(true); }));
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_EQ(1, rph.bad_msg_count());
|
| +}
|
| +
|
| +} // namespace content
|
|
|