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 "chrome/browser/media/desktop_media_picker_model.h" 6 7 #include "base/message_loop/message_loop.h" 8 #include "base/stl_util.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "base/synchronization/lock.h" 11 #include "content/public/test/test_browser_thread.h" 12 #include "testing/gmock/include/gmock/gmock.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 15 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" 16 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h" 17 18 using testing::_; 19 using testing::AtMost; 20 using testing::DoAll; 21 22 namespace { 23 24 class MockObserver : public DesktopMediaPickerModel::Observer { 25 public: 26 MOCK_METHOD1(OnSourceAdded, void(int index)); 27 MOCK_METHOD1(OnSourceRemoved, void(int index)); 28 MOCK_METHOD1(OnSourceNameChanged, void(int index)); 29 MOCK_METHOD1(OnSourceThumbnailChanged, void(int index)); 30 }; 31 32 class FakeScreenCapturer : public webrtc::ScreenCapturer { 33 public: 34 FakeScreenCapturer() {} 35 virtual ~FakeScreenCapturer() {} 36 37 // webrtc::ScreenCapturer implementation. 38 virtual void Start(Callback* callback) OVERRIDE { 39 callback_ = callback; 40 } 41 42 virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE { 43 DCHECK(callback_); 44 webrtc::DesktopFrame* frame = 45 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10)); 46 memset(frame->data(), 0, frame->stride() * frame->size().height()); 47 callback_->OnCaptureCompleted(frame); 48 } 49 50 virtual void SetMouseShapeObserver( 51 MouseShapeObserver* mouse_shape_observer) OVERRIDE { 52 NOTIMPLEMENTED(); 53 } 54 55 protected: 56 Callback* callback_; 57 58 DISALLOW_COPY_AND_ASSIGN(FakeScreenCapturer); 59 }; 60 61 class FakeWindowCapturer : public webrtc::WindowCapturer { 62 public: 63 FakeWindowCapturer() 64 : callback_(NULL) { 65 } 66 virtual ~FakeWindowCapturer() { 67 STLDeleteContainerPairSecondPointers(frames_.begin(), frames_.end()); 68 } 69 70 void SetWindowList(const WindowList& list) { 71 base::AutoLock lock(window_list_lock_); 72 window_list_ = list; 73 } 74 75 void SetNextFrame(WindowId window_id, 76 scoped_ptr<webrtc::DesktopFrame> frame) { 77 frames_[window_id] = frame.release(); 78 } 79 80 // webrtc::WindowCapturer implementation. 81 virtual void Start(Callback* callback) OVERRIDE { 82 callback_ = callback; 83 } 84 85 virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE { 86 DCHECK(callback_); 87 88 webrtc::DesktopFrame* frame; 89 std::map<WindowId, webrtc::DesktopFrame*>::iterator it = 90 frames_.find(selected_window_id_); 91 if (it == frames_.end()) { 92 frame = new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10)); 93 memset(frame->data(), 0, frame->stride() * frame->size().height()); 94 } else { 95 frame = it->second; 96 frames_.erase(it); 97 } 98 callback_->OnCaptureCompleted(frame); 99 } 100 101 virtual bool GetWindowList(WindowList* windows) OVERRIDE { 102 base::AutoLock lock(window_list_lock_); 103 *windows = window_list_; 104 return true; 105 } 106 107 virtual bool SelectWindow(WindowId id) OVERRIDE { 108 selected_window_id_ = id; 109 return true; 110 } 111 112 private: 113 Callback* callback_; 114 WindowList window_list_; 115 base::Lock window_list_lock_; 116 117 WindowId selected_window_id_; 118 119 // Frames to be captured per window. 120 std::map<WindowId, webrtc::DesktopFrame*> frames_; 121 122 DISALLOW_COPY_AND_ASSIGN(FakeWindowCapturer); 123 }; 124 125 class DesktopMediaPickerModelTest : public testing::Test { 126 public: 127 DesktopMediaPickerModelTest() 128 : window_capturer_(NULL), 129 ui_thread_(content::BrowserThread::UI, 130 &message_loop_) { 131 // Set update period to reduce the time it takes to run tests. 132 model_.SetUpdatePeriod(base::TimeDelta::FromMilliseconds(0)); 133 } 134 135 void SetDefaultCapturers() { 136 window_capturer_ = new FakeWindowCapturer(); 137 model_.SetCapturers( 138 scoped_ptr<webrtc::ScreenCapturer>(new FakeScreenCapturer()), 139 scoped_ptr<webrtc::WindowCapturer>(window_capturer_)); 140 } 141 142 protected: 143 // Must be listed before |model_|, so it's destroyed last. 144 MockObserver observer_; 145 146 // Owned by |model_|; 147 FakeWindowCapturer* window_capturer_; 148 149 DesktopMediaPickerModel model_; 150 151 base::MessageLoop message_loop_; 152 content::TestBrowserThread ui_thread_; 153 154 DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerModelTest); 155 }; 156 157 ACTION_P2(CheckListSize, model, expected_list_size) { 158 EXPECT_EQ(expected_list_size, model->source_count()); 159 } 160 161 ACTION_P(QuitMessageLoop, message_loop) { 162 message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 163 } 164 165 TEST_F(DesktopMediaPickerModelTest, InitialSourceList) { 166 SetDefaultCapturers(); 167 168 webrtc::WindowCapturer::WindowList list; 169 webrtc::WindowCapturer::Window window; 170 window.id = 0; 171 window.title = "Test window"; 172 list.push_back(window); 173 window_capturer_->SetWindowList(list); 174 175 { 176 testing::InSequence dummy; 177 EXPECT_CALL(observer_, OnSourceAdded(0)) 178 .WillOnce(CheckListSize(&model_, 1)); 179 EXPECT_CALL(observer_, OnSourceAdded(1)) 180 .WillOnce(CheckListSize(&model_, 2)); 181 EXPECT_CALL(observer_, OnSourceThumbnailChanged(0)); 182 EXPECT_CALL(observer_, OnSourceThumbnailChanged(1)) 183 .WillOnce(QuitMessageLoop(&message_loop_)); 184 } 185 model_.StartUpdating(&observer_); 186 187 message_loop_.Run(); 188 189 EXPECT_EQ(model_.source(0).id.type, content::DesktopMediaID::TYPE_SCREEN); 190 EXPECT_EQ(model_.source(0).id.id, 0); 191 EXPECT_EQ(model_.source(1).id.type, content::DesktopMediaID::TYPE_WINDOW); 192 EXPECT_EQ(model_.source(1).id.id, 0); 193 EXPECT_EQ(model_.source(1).name, UTF8ToUTF16(window.title)); 194 } 195 196 TEST_F(DesktopMediaPickerModelTest, WindowsOnly) { 197 window_capturer_ = new FakeWindowCapturer(); 198 model_.SetCapturers( 199 scoped_ptr<webrtc::ScreenCapturer>(), 200 scoped_ptr<webrtc::WindowCapturer>(window_capturer_)); 201 202 webrtc::WindowCapturer::WindowList list; 203 webrtc::WindowCapturer::Window window; 204 window.id = 0; 205 window.title = "Test window"; 206 list.push_back(window); 207 window_capturer_->SetWindowList(list); 208 209 { 210 testing::InSequence dummy; 211 EXPECT_CALL(observer_, OnSourceAdded(0)) 212 .WillOnce(CheckListSize(&model_, 1)); 213 EXPECT_CALL(observer_, OnSourceThumbnailChanged(0)) 214 .WillOnce(QuitMessageLoop(&message_loop_)); 215 } 216 model_.StartUpdating(&observer_); 217 218 message_loop_.Run(); 219 220 EXPECT_EQ(model_.source(0).id.type, content::DesktopMediaID::TYPE_WINDOW); 221 } 222 223 TEST_F(DesktopMediaPickerModelTest, ScreenOnly) { 224 model_.SetCapturers( 225 scoped_ptr<webrtc::ScreenCapturer>(new FakeScreenCapturer), 226 scoped_ptr<webrtc::WindowCapturer>()); 227 228 { 229 testing::InSequence dummy; 230 EXPECT_CALL(observer_, OnSourceAdded(0)) 231 .WillOnce(CheckListSize(&model_, 1)); 232 EXPECT_CALL(observer_, OnSourceThumbnailChanged(0)) 233 .WillOnce(QuitMessageLoop(&message_loop_)); 234 } 235 model_.StartUpdating(&observer_); 236 237 message_loop_.Run(); 238 239 EXPECT_EQ(model_.source(0).id.type, content::DesktopMediaID::TYPE_SCREEN); 240 } 241 242 TEST_F(DesktopMediaPickerModelTest, AddWindow) { 243 SetDefaultCapturers(); 244 245 webrtc::WindowCapturer::WindowList list; 246 webrtc::WindowCapturer::Window window; 247 window.id = 1; 248 window.title = "Test window 1"; 249 list.push_back(window); 250 window_capturer_->SetWindowList(list); 251 252 { 253 testing::InSequence dummy; 254 EXPECT_CALL(observer_, OnSourceAdded(0)) 255 .WillOnce(CheckListSize(&model_, 1)); 256 EXPECT_CALL(observer_, OnSourceAdded(1)) 257 .WillOnce(CheckListSize(&model_, 2)); 258 EXPECT_CALL(observer_, OnSourceThumbnailChanged(0)); 259 EXPECT_CALL(observer_, OnSourceThumbnailChanged(1)) 260 .WillOnce(QuitMessageLoop(&message_loop_)); 261 } 262 model_.StartUpdating(&observer_); 263 264 message_loop_.Run(); 265 266 testing::Mock::VerifyAndClearExpectations(&observer_); 267 268 EXPECT_CALL(observer_, OnSourceAdded(1)) 269 .WillOnce(DoAll(CheckListSize(&model_, 3), 270 QuitMessageLoop(&message_loop_))); 271 272 window.id = 0; 273 window.title = "Test window 0"; 274 list.push_back(window); 275 window_capturer_->SetWindowList(list); 276 277 message_loop_.Run(); 278 279 EXPECT_EQ(model_.source(1).id.type, content::DesktopMediaID::TYPE_WINDOW); 280 EXPECT_EQ(model_.source(1).id.id, 0); 281 } 282 283 TEST_F(DesktopMediaPickerModelTest, RemoveWindow) { 284 SetDefaultCapturers(); 285 286 webrtc::WindowCapturer::WindowList list; 287 webrtc::WindowCapturer::Window window; 288 window.id = 0; 289 window.title = "Test window 0"; 290 list.push_back(window); 291 window.id = 1; 292 window.title = "Test window 1"; 293 list.push_back(window); 294 window_capturer_->SetWindowList(list); 295 296 { 297 testing::InSequence dummy; 298 EXPECT_CALL(observer_, OnSourceAdded(0)) 299 .WillOnce(CheckListSize(&model_, 1)); 300 EXPECT_CALL(observer_, OnSourceAdded(1)) 301 .WillOnce(CheckListSize(&model_, 2)); 302 EXPECT_CALL(observer_, OnSourceAdded(2)) 303 .WillOnce(CheckListSize(&model_, 3)); 304 EXPECT_CALL(observer_, OnSourceThumbnailChanged(0)); 305 EXPECT_CALL(observer_, OnSourceThumbnailChanged(1)); 306 EXPECT_CALL(observer_, OnSourceThumbnailChanged(2)) 307 .WillOnce(QuitMessageLoop(&message_loop_)); 308 } 309 model_.StartUpdating(&observer_); 310 311 message_loop_.Run(); 312 313 testing::Mock::VerifyAndClearExpectations(&observer_); 314 315 EXPECT_CALL(observer_, OnSourceRemoved(1)) 316 .WillOnce(DoAll(CheckListSize(&model_, 2), 317 QuitMessageLoop(&message_loop_))); 318 319 list.erase(list.begin()); 320 window_capturer_->SetWindowList(list); 321 322 message_loop_.Run(); 323 } 324 325 TEST_F(DesktopMediaPickerModelTest, UpdateTitle) { 326 SetDefaultCapturers(); 327 328 webrtc::WindowCapturer::WindowList list; 329 webrtc::WindowCapturer::Window window; 330 window.id = 0; 331 window.title = "Test window"; 332 list.push_back(window); 333 window_capturer_->SetWindowList(list); 334 335 { 336 testing::InSequence dummy; 337 EXPECT_CALL(observer_, OnSourceAdded(0)) 338 .WillOnce(CheckListSize(&model_, 1)); 339 EXPECT_CALL(observer_, OnSourceAdded(1)) 340 .WillOnce(CheckListSize(&model_, 2)); 341 EXPECT_CALL(observer_, OnSourceThumbnailChanged(0)); 342 EXPECT_CALL(observer_, OnSourceThumbnailChanged(1)) 343 .WillOnce(QuitMessageLoop(&message_loop_)); 344 } 345 model_.StartUpdating(&observer_); 346 347 message_loop_.Run(); 348 349 testing::Mock::VerifyAndClearExpectations(&observer_); 350 351 EXPECT_CALL(observer_, OnSourceNameChanged(1)) 352 .WillOnce(QuitMessageLoop(&message_loop_)); 353 354 const std::string kTestTitle = "New Title"; 355 356 list[0].title = kTestTitle; 357 window_capturer_->SetWindowList(list); 358 359 message_loop_.Run(); 360 361 EXPECT_EQ(model_.source(1).name, base::UTF8ToUTF16(kTestTitle)); 362 } 363 364 TEST_F(DesktopMediaPickerModelTest, UpdateThumbnail) { 365 SetDefaultCapturers(); 366 367 webrtc::WindowCapturer::WindowList list; 368 webrtc::WindowCapturer::Window window; 369 window.id = 0; 370 window.title = "Test window 1"; 371 list.push_back(window); 372 window.id = 1; 373 window.title = "Test window 2"; 374 list.push_back(window); 375 window_capturer_->SetWindowList(list); 376 377 { 378 testing::InSequence dummy; 379 EXPECT_CALL(observer_, OnSourceAdded(0)) 380 .WillOnce(CheckListSize(&model_, 1)); 381 EXPECT_CALL(observer_, OnSourceAdded(1)) 382 .WillOnce(CheckListSize(&model_, 2)); 383 EXPECT_CALL(observer_, OnSourceAdded(2)) 384 .WillOnce(CheckListSize(&model_, 3)); 385 EXPECT_CALL(observer_, OnSourceThumbnailChanged(0)); 386 EXPECT_CALL(observer_, OnSourceThumbnailChanged(1)); 387 EXPECT_CALL(observer_, OnSourceThumbnailChanged(2)) 388 .WillOnce(QuitMessageLoop(&message_loop_)); 389 } 390 model_.StartUpdating(&observer_); 391 392 message_loop_.Run(); 393 394 testing::Mock::VerifyAndClearExpectations(&observer_); 395 396 EXPECT_CALL(observer_, OnSourceThumbnailChanged(1)) 397 .WillOnce(QuitMessageLoop(&message_loop_)); 398 399 // Update frame for the window and verify that we get notification about it. 400 scoped_ptr<webrtc::DesktopFrame> frame( 401 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10))); 402 memset(frame->data(), 1, frame->stride() * frame->size().height()); 403 window_capturer_->SetNextFrame(0, frame.Pass()); 404 405 message_loop_.Run(); 406 } 407 408 } // namespace 409