1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/strings/string_number_conversions.h" 6 #include "content/browser/renderer_host/media/device_request_message_filter.h" 7 #include "content/browser/renderer_host/media/media_stream_manager.h" 8 #include "content/common/media/media_stream_messages.h" 9 #include "content/public/test/mock_resource_context.h" 10 #include "content/public/test/test_browser_thread.h" 11 #include "testing/gmock/include/gmock/gmock.h" 12 #include "testing/gtest/include/gtest/gtest.h" 13 14 using ::testing::_; 15 using ::testing::Invoke; 16 17 namespace content { 18 19 static const std::string kAudioLabel = "audio_label"; 20 static const std::string kVideoLabel = "video_label"; 21 22 class MockMediaStreamManager : public MediaStreamManager { 23 public: 24 MockMediaStreamManager() {} 25 26 virtual ~MockMediaStreamManager() {} 27 28 MOCK_METHOD6(EnumerateDevices, 29 std::string(MediaStreamRequester* requester, 30 int render_process_id, 31 int render_view_id, 32 int page_request_id, 33 MediaStreamType type, 34 const GURL& security_origin)); 35 MOCK_METHOD1(StopGeneratedStream, void(const std::string& label)); 36 37 std::string DoEnumerateDevices(MediaStreamRequester* requester, 38 int render_process_id, 39 int render_view_id, 40 int page_request_id, 41 MediaStreamType type, 42 const GURL& security_origin) { 43 if (type == MEDIA_DEVICE_AUDIO_CAPTURE) { 44 return kAudioLabel; 45 } else { 46 return kVideoLabel; 47 } 48 } 49 }; 50 51 class MockDeviceRequestMessageFilter : public DeviceRequestMessageFilter { 52 public: 53 MockDeviceRequestMessageFilter(MockResourceContext* context, 54 MockMediaStreamManager* manager) 55 : DeviceRequestMessageFilter(context, manager), received_id_(-1) {} 56 StreamDeviceInfoArray requested_devices() { return requested_devices_; } 57 int received_id() { return received_id_; } 58 59 private: 60 virtual ~MockDeviceRequestMessageFilter() {} 61 62 // Override the Send() method to intercept the message that we're sending to 63 // the renderer. 64 virtual bool Send(IPC::Message* reply_msg) OVERRIDE { 65 CHECK(reply_msg); 66 67 bool handled = true; 68 IPC_BEGIN_MESSAGE_MAP(MockDeviceRequestMessageFilter, *reply_msg) 69 IPC_MESSAGE_HANDLER(MediaStreamMsg_GetSourcesACK, SaveDevices) 70 IPC_MESSAGE_UNHANDLED(handled = false) 71 IPC_END_MESSAGE_MAP() 72 EXPECT_TRUE(handled); 73 74 delete reply_msg; 75 return true; 76 } 77 78 void SaveDevices(int request_id, const StreamDeviceInfoArray& devices) { 79 received_id_ = request_id; 80 requested_devices_ = devices; 81 } 82 83 int received_id_; 84 StreamDeviceInfoArray requested_devices_; 85 }; 86 87 class DeviceRequestMessageFilterTest : public testing::Test { 88 public: 89 DeviceRequestMessageFilterTest() : next_device_id_(0) {} 90 91 void RunTest(int number_audio_devices, int number_video_devices) { 92 AddAudioDevices(number_audio_devices); 93 AddVideoDevices(number_video_devices); 94 GURL origin("https://test.com"); 95 EXPECT_CALL(*media_stream_manager_, 96 EnumerateDevices(_, _, _, _, MEDIA_DEVICE_AUDIO_CAPTURE, _)) 97 .Times(1); 98 EXPECT_CALL(*media_stream_manager_, 99 EnumerateDevices(_, _, _, _, MEDIA_DEVICE_VIDEO_CAPTURE, _)) 100 .Times(1); 101 // Send message to get devices. Should trigger 2 EnumerateDevice() requests. 102 const int kRequestId = 123; 103 SendGetSourcesMessage(kRequestId, origin); 104 105 // Run audio callback. Because there's still an outstanding video request, 106 // this should not populate |message|. 107 FireAudioDeviceCallback(); 108 EXPECT_EQ(0u, host_->requested_devices().size()); 109 110 // After the video device callback is fired, |message| should be populated. 111 EXPECT_CALL(*media_stream_manager_, StopGeneratedStream(kAudioLabel)) 112 .Times(1); 113 EXPECT_CALL(*media_stream_manager_, StopGeneratedStream(kVideoLabel)) 114 .Times(1); 115 FireVideoDeviceCallback(); 116 EXPECT_EQ(static_cast<size_t>(number_audio_devices + number_video_devices), 117 host_->requested_devices().size()); 118 119 EXPECT_EQ(kRequestId, host_->received_id()); 120 // Check to make sure no devices have raw ids. 121 EXPECT_FALSE(DoesContainRawIds(host_->requested_devices())); 122 123 // Check to make sure every GUID produced matches a raw device id. 124 EXPECT_TRUE(DoesEveryDeviceMapToRawId(host_->requested_devices(), origin)); 125 } 126 127 bool AreLabelsPresent(MediaStreamType type) { 128 const StreamDeviceInfoArray& devices = host_->requested_devices(); 129 for (size_t i = 0; i < devices.size(); i++) { 130 if (devices[i].device.type == type && !devices[i].device.name.empty()) 131 return true; 132 } 133 return false; 134 } 135 136 protected: 137 virtual ~DeviceRequestMessageFilterTest() {} 138 139 virtual void SetUp() OVERRIDE { 140 message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO)); 141 io_thread_.reset( 142 new TestBrowserThread(BrowserThread::IO, message_loop_.get())); 143 144 media_stream_manager_.reset(new MockMediaStreamManager()); 145 ON_CALL(*media_stream_manager_, EnumerateDevices(_, _, _, _, _, _)) 146 .WillByDefault(Invoke(media_stream_manager_.get(), 147 &MockMediaStreamManager::DoEnumerateDevices)); 148 149 resource_context_.reset(new MockResourceContext(NULL)); 150 host_ = new MockDeviceRequestMessageFilter(resource_context_.get(), 151 media_stream_manager_.get()); 152 } 153 154 scoped_refptr<MockDeviceRequestMessageFilter> host_; 155 scoped_ptr<MockMediaStreamManager> media_stream_manager_; 156 scoped_ptr<MockResourceContext> resource_context_; 157 StreamDeviceInfoArray physical_audio_devices_; 158 StreamDeviceInfoArray physical_video_devices_; 159 scoped_ptr<base::MessageLoop> message_loop_; 160 scoped_ptr<TestBrowserThread> io_thread_; 161 162 private: 163 void AddAudioDevices(int number_of_devices) { 164 for (int i = 0; i < number_of_devices; i++) { 165 physical_audio_devices_.push_back( 166 StreamDeviceInfo(MEDIA_DEVICE_AUDIO_CAPTURE, 167 "/dev/audio/" + base::IntToString(next_device_id_), 168 "Audio Device" + base::IntToString(next_device_id_), 169 false)); 170 next_device_id_++; 171 } 172 } 173 174 void AddVideoDevices(int number_of_devices) { 175 for (int i = 0; i < number_of_devices; i++) { 176 physical_video_devices_.push_back( 177 StreamDeviceInfo(MEDIA_DEVICE_VIDEO_CAPTURE, 178 "/dev/video/" + base::IntToString(next_device_id_), 179 "Video Device" + base::IntToString(next_device_id_), 180 false)); 181 next_device_id_++; 182 } 183 } 184 185 void SendGetSourcesMessage(int request_id, const GURL& origin) { 186 // Since we're not actually sending IPC messages, this is a throw-away 187 // value. 188 bool message_was_ok; 189 host_->OnMessageReceived(MediaStreamHostMsg_GetSources(request_id, origin), 190 &message_was_ok); 191 } 192 193 void FireAudioDeviceCallback() { 194 host_->DevicesEnumerated(kAudioLabel, physical_audio_devices_); 195 } 196 197 void FireVideoDeviceCallback() { 198 host_->DevicesEnumerated(kVideoLabel, physical_video_devices_); 199 } 200 201 bool DoesContainRawIds(const StreamDeviceInfoArray& devices) { 202 for (size_t i = 0; i < devices.size(); i++) { 203 for (size_t j = 0; j < physical_audio_devices_.size(); ++j) { 204 if (physical_audio_devices_[j].device.id == devices[i].device.id) 205 return true; 206 } 207 for (size_t j = 0; j < physical_video_devices_.size(); ++j) { 208 if (physical_video_devices_[j].device.id == devices[i].device.id) 209 return true; 210 } 211 } 212 return false; 213 } 214 215 bool DoesEveryDeviceMapToRawId(const StreamDeviceInfoArray& devices, 216 const GURL& origin) { 217 for (size_t i = 0; i < devices.size(); i++) { 218 bool found_match = false; 219 for (size_t j = 0; j < physical_audio_devices_.size(); ++j) { 220 if (DeviceRequestMessageFilter::DoesRawIdMatchGuid( 221 origin, 222 devices[i].device.id, 223 physical_audio_devices_[j].device.id)) { 224 EXPECT_FALSE(found_match); 225 found_match = true; 226 } 227 } 228 for (size_t j = 0; j < physical_video_devices_.size(); ++j) { 229 if (DeviceRequestMessageFilter::DoesRawIdMatchGuid( 230 origin, 231 devices[i].device.id, 232 physical_video_devices_[j].device.id)) { 233 EXPECT_FALSE(found_match); 234 found_match = true; 235 } 236 } 237 if (!found_match) 238 return false; 239 } 240 return true; 241 } 242 243 int next_device_id_; 244 }; 245 246 TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AudioAndVideoDevices) { 247 // Runs through test with 1 audio and 1 video device. 248 RunTest(1, 1); 249 } 250 251 TEST_F(DeviceRequestMessageFilterTest, 252 TestGetSources_MultipleAudioAndVideoDevices) { 253 // Runs through test with 3 audio devices and 2 video devices. 254 RunTest(3, 2); 255 } 256 257 TEST_F(DeviceRequestMessageFilterTest, TestGetSources_NoVideoDevices) { 258 // Runs through test with 4 audio devices and 0 video devices. 259 RunTest(4, 0); 260 } 261 262 TEST_F(DeviceRequestMessageFilterTest, TestGetSources_NoAudioDevices) { 263 // Runs through test with 0 audio devices and 3 video devices. 264 RunTest(0, 3); 265 } 266 267 TEST_F(DeviceRequestMessageFilterTest, TestGetSources_NoDevices) { 268 // Runs through test with no devices. 269 RunTest(0, 0); 270 } 271 272 TEST_F(DeviceRequestMessageFilterTest, TestGetSources_DenyMicDenyCamera) { 273 resource_context_->set_mic_access(false); 274 resource_context_->set_camera_access(false); 275 RunTest(3, 3); 276 EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE)); 277 EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); 278 } 279 280 TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AllowMicDenyCamera) { 281 resource_context_->set_mic_access(true); 282 resource_context_->set_camera_access(false); 283 RunTest(3, 3); 284 EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE)); 285 EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); 286 } 287 288 TEST_F(DeviceRequestMessageFilterTest, TestGetSources_DenyMicAllowCamera) { 289 resource_context_->set_mic_access(false); 290 resource_context_->set_camera_access(true); 291 RunTest(3, 3); 292 EXPECT_FALSE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE)); 293 EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); 294 } 295 296 TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AllowMicAllowCamera) { 297 resource_context_->set_mic_access(true); 298 resource_context_->set_camera_access(true); 299 RunTest(3, 3); 300 EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_AUDIO_CAPTURE)); 301 EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); 302 } 303 304 }; // namespace content 305