| // Copyright 2013 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 "base/strings/string_number_conversions.h" |
| #include "content/browser/renderer_host/media/device_request_message_filter.h" |
| #include "content/browser/renderer_host/media/media_stream_manager.h" |
| #include "content/common/media/media_stream_messages.h" |
| #include "content/public/browser/media_device_id.h" |
| #include "content/public/test/mock_resource_context.h" |
| #include "content/public/test/test_browser_thread.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::_; |
| using ::testing::Invoke; |
| |
| namespace content { |
| |
| static const std::string kAudioLabel = "audio_label"; |
| static const std::string kVideoLabel = "video_label"; |
| |
| class MockMediaStreamManager : public MediaStreamManager { |
| public: |
| MockMediaStreamManager() {} |
| |
| virtual ~MockMediaStreamManager() {} |
| |
| MOCK_METHOD6(EnumerateDevices, |
| std::string(MediaStreamRequester* requester, |
| int render_process_id, |
| int render_view_id, |
| int page_request_id, |
| MediaStreamType type, |
| const GURL& security_origin)); |
| MOCK_METHOD1(CancelRequest, void(const std::string& label)); |
| |
| std::string DoEnumerateDevices(MediaStreamRequester* requester, |
| int render_process_id, |
| int render_view_id, |
| int page_request_id, |
| MediaStreamType type, |
| const GURL& security_origin) { |
| if (type == MEDIA_DEVICE_AUDIO_CAPTURE) { |
| return kAudioLabel; |
| } else { |
| return kVideoLabel; |
| } |
| } |
| }; |
| |
| class MockDeviceRequestMessageFilter : public DeviceRequestMessageFilter { |
| public: |
| MockDeviceRequestMessageFilter(MockResourceContext* context, |
| MockMediaStreamManager* manager) |
| : DeviceRequestMessageFilter(context, manager), received_id_(-1) {} |
| StreamDeviceInfoArray requested_devices() { return requested_devices_; } |
| int received_id() { return received_id_; } |
| |
| private: |
| virtual ~MockDeviceRequestMessageFilter() {} |
| |
| // Override the Send() method to intercept the message that we're sending to |
| // the renderer. |
| virtual bool Send(IPC::Message* reply_msg) OVERRIDE { |
| CHECK(reply_msg); |
| |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(MockDeviceRequestMessageFilter, *reply_msg) |
| IPC_MESSAGE_HANDLER(MediaStreamMsg_GetSourcesACK, SaveDevices) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| EXPECT_TRUE(handled); |
| |
| delete reply_msg; |
| return true; |
| } |
| |
| void SaveDevices(int request_id, const StreamDeviceInfoArray& devices) { |
| received_id_ = request_id; |
| requested_devices_ = devices; |
| } |
| |
| int received_id_; |
| StreamDeviceInfoArray requested_devices_; |
| }; |
| |
| class DeviceRequestMessageFilterTest : public testing::Test { |
| public: |
| DeviceRequestMessageFilterTest() : next_device_id_(0) {} |
| |
| void RunTest(int number_audio_devices, int number_video_devices) { |
| AddAudioDevices(number_audio_devices); |
| AddVideoDevices(number_video_devices); |
| GURL origin("https://test.com"); |
| EXPECT_CALL(*media_stream_manager_, |
| EnumerateDevices(_, _, _, _, MEDIA_DEVICE_AUDIO_CAPTURE, _)) |
| .Times(1); |
| EXPECT_CALL(*media_stream_manager_, |
| EnumerateDevices(_, _, _, _, MEDIA_DEVICE_VIDEO_CAPTURE, _)) |
| .Times(1); |
| // Send message to get devices. Should trigger 2 EnumerateDevice() requests. |
| const int kRequestId = 123; |
| SendGetSourcesMessage(kRequestId, origin); |
| |
| // Run audio callback. Because there's still an outstanding video request, |
| // this should not populate |message|. |
| FireAudioDeviceCallback(); |
| EXPECT_EQ(0u, host_->requested_devices().size()); |
| |
| // After the video device callback is fired, |message| should be populated. |
| EXPECT_CALL(*media_stream_manager_, CancelRequest(kAudioLabel)) |
| .Times(1); |
| EXPECT_CALL(*media_stream_manager_, CancelRequest(kVideoLabel)) |
| .Times(1); |
| FireVideoDeviceCallback(); |
| EXPECT_EQ(static_cast<size_t>(number_audio_devices + number_video_devices), |
| host_->requested_devices().size()); |
| |
| EXPECT_EQ(kRequestId, host_->received_id()); |
| // Check to make sure no devices have raw ids. |
| EXPECT_FALSE(DoesContainRawIds(host_->requested_devices())); |
| |
| // Check to make sure every GUID produced matches a raw device id. |
| EXPECT_TRUE(DoesEveryDeviceMapToRawId(host_->requested_devices(), origin)); |
| } |
| |
| bool AreLabelsPresent(MediaStreamType type) { |
| const StreamDeviceInfoArray& devices = host_->requested_devices(); |
| for (size_t i = 0; i < devices.size(); i++) { |
| if (devices[i].device.type == type && !devices[i].device.name.empty()) |
| return true; |
| } |
| return false; |
| } |
| |
| protected: |
| virtual ~DeviceRequestMessageFilterTest() {} |
| |
| virtual void SetUp() OVERRIDE { |
| message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO)); |
| io_thread_.reset( |
| new TestBrowserThread(BrowserThread::IO, message_loop_.get())); |
| |
| media_stream_manager_.reset(new MockMediaStreamManager()); |
| ON_CALL(*media_stream_manager_, EnumerateDevices(_, _, _, _, _, _)) |
| .WillByDefault(Invoke(media_stream_manager_.get(), |
| &MockMediaStreamManager::DoEnumerateDevices)); |
| |
| resource_context_.reset(new MockResourceContext(NULL)); |
| host_ = new MockDeviceRequestMessageFilter(resource_context_.get(), |
| media_stream_manager_.get()); |
| } |
| |
| scoped_refptr<MockDeviceRequestMessageFilter> host_; |
| scoped_ptr<MockMediaStreamManager> media_stream_manager_; |
| scoped_ptr<MockResourceContext> resource_context_; |
| StreamDeviceInfoArray physical_audio_devices_; |
| StreamDeviceInfoArray physical_video_devices_; |
| scoped_ptr<base::MessageLoop> message_loop_; |
| scoped_ptr<TestBrowserThread> io_thread_; |
| |
| private: |
| void AddAudioDevices(int number_of_devices) { |
| for (int i = 0; i < number_of_devices; i++) { |
| physical_audio_devices_.push_back( |
| StreamDeviceInfo( |
| MEDIA_DEVICE_AUDIO_CAPTURE, |
| "/dev/audio/" + base::IntToString(next_device_id_), |
| "Audio Device" + base::IntToString(next_device_id_))); |
| next_device_id_++; |
| } |
| } |
| |
| void AddVideoDevices(int number_of_devices) { |
| for (int i = 0; i < number_of_devices; i++) { |
| physical_video_devices_.push_back( |
| StreamDeviceInfo( |
| MEDIA_DEVICE_VIDEO_CAPTURE, |
| "/dev/video/" + base::IntToString(next_device_id_), |
| "Video Device" + base::IntToString(next_device_id_))); |
| next_device_id_++; |
| } |
| } |
| |
| void SendGetSourcesMessage(int request_id, const GURL& origin) { |
| // Since we're not actually sending IPC messages, this is a throw-away |
| // value. |
| bool message_was_ok; |
| host_->OnMessageReceived(MediaStreamHostMsg_GetSources(request_id, origin), |
| &message_was_ok); |
| } |
| |
| void FireAudioDeviceCallback() { |
| host_->DevicesEnumerated(kAudioLabel, physical_audio_devices_); |
| } |
| |
| void FireVideoDeviceCallback() { |
| host_->DevicesEnumerated(kVideoLabel, physical_video_devices_); |
| } |
| |
| bool DoesContainRawIds(const StreamDeviceInfoArray& devices) { |
| for (size_t i = 0; i < devices.size(); i++) { |
| for (size_t j = 0; j < physical_audio_devices_.size(); ++j) { |
| if (physical_audio_devices_[j].device.id == devices[i].device.id) |
| return true; |
| } |
| for (size_t j = 0; j < physical_video_devices_.size(); ++j) { |
| if (physical_video_devices_[j].device.id == devices[i].device.id) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool DoesEveryDeviceMapToRawId(const StreamDeviceInfoArray& devices, |
| const GURL& origin) { |
| for (size_t i = 0; i < devices.size(); i++) { |
| bool found_match = false; |
| for (size_t j = 0; j < physical_audio_devices_.size(); ++j) { |
| if (content::DoesMediaDeviceIDMatchHMAC( |
| origin, |
| devices[i].device.id, |
| physical_audio_devices_[j].device.id)) { |
| EXPECT_FALSE(found_match); |
| found_match = true; |
| } |
| } |
| for (size_t j = 0; j < physical_video_devices_.size(); ++j) { |
| if (content::DoesMediaDeviceIDMatchHMAC( |
| origin, |
| devices[i].device.id, |
| physical_video_devices_[j].device.id)) { |
| EXPECT_FALSE(found_match); |
| found_match = true; |
| } |
| } |
| if (!found_match) |
| return false; |
| } |
| return true; |
| } |
| |
| int next_device_id_; |
| }; |
| |
| TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AudioAndVideoDevices) { |
| // Runs through test with 1 audio and 1 video device. |
| RunTest(1, 1); |
| } |
| |
| TEST_F(DeviceRequestMessageFilterTest, |
| TestGetSources_MultipleAudioAndVideoDevices) { |
| // Runs through test with 3 audio devices and 2 video devices. |
| RunTest(3, 2); |
| } |
| |
| TEST_F(DeviceRequestMessageFilterTest, TestGetSources_NoVideoDevices) { |
| // Runs through test with 4 audio devices and 0 video devices. |
| RunTest(4, 0); |
| } |
| |
| TEST_F(DeviceRequestMessageFilterTest, TestGetSources_NoAudioDevices) { |
| // Runs through test with 0 audio devices and 3 video devices. |
| RunTest(0, 3); |
| } |
| |
| TEST_F(DeviceRequestMessageFilterTest, TestGetSources_NoDevices) { |
| // Runs through test with no devices. |
| RunTest(0, 0); |
| } |
| |
| TEST_F(DeviceRequestMessageFilterTest, TestGetSources_DenyMicDenyCamera) { |
| resource_context_->set_mic_access(false); |
| resource_context_->set_camera_access(false); |
| RunTest(3, 3); |
| EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE)); |
| EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); |
| } |
| |
| TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AllowMicDenyCamera) { |
| resource_context_->set_mic_access(true); |
| resource_context_->set_camera_access(false); |
| RunTest(3, 3); |
| EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE)); |
| EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); |
| } |
| |
| TEST_F(DeviceRequestMessageFilterTest, TestGetSources_DenyMicAllowCamera) { |
| resource_context_->set_mic_access(false); |
| resource_context_->set_camera_access(true); |
| RunTest(3, 3); |
| EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE)); |
| EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); |
| } |
| |
| TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AllowMicAllowCamera) { |
| resource_context_->set_mic_access(true); |
| resource_context_->set_camera_access(true); |
| RunTest(3, 3); |
| EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE)); |
| EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); |
| } |
| |
| }; // namespace content |