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/native_desktop_media_list.h"
6
7 #include "base/message_loop/message_loop.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/synchronization/lock.h"
10 #include "chrome/browser/media/desktop_media_list_observer.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::DoAll;
20
21 namespace {
22
23 class MockObserver : public DesktopMediaListObserver {
24 public:
25 MOCK_METHOD1(OnSourceAdded, void(int index));
26 MOCK_METHOD1(OnSourceRemoved, void(int index));
27 MOCK_METHOD2(OnSourceMoved, void(int old_index, int new_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:
FakeScreenCapturer()34 FakeScreenCapturer() {}
~FakeScreenCapturer()35 virtual ~FakeScreenCapturer() {}
36
37 // webrtc::ScreenCapturer implementation.
Start(Callback * callback)38 virtual void Start(Callback* callback) OVERRIDE {
39 callback_ = callback;
40 }
41
Capture(const webrtc::DesktopRegion & region)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
SetMouseShapeObserver(MouseShapeObserver * mouse_shape_observer)50 virtual void SetMouseShapeObserver(
51 MouseShapeObserver* mouse_shape_observer) OVERRIDE {
52 NOTIMPLEMENTED();
53 }
54
GetScreenList(ScreenList * screens)55 virtual bool GetScreenList(ScreenList* screens) OVERRIDE {
56 webrtc::ScreenCapturer::Screen screen;
57 screen.id = 0;
58 screens->push_back(screen);
59 return true;
60 }
61
SelectScreen(webrtc::ScreenId id)62 virtual bool SelectScreen(webrtc::ScreenId id) OVERRIDE {
63 EXPECT_EQ(0, id);
64 return true;
65 }
66
67 protected:
68 Callback* callback_;
69
70 DISALLOW_COPY_AND_ASSIGN(FakeScreenCapturer);
71 };
72
73 class FakeWindowCapturer : public webrtc::WindowCapturer {
74 public:
FakeWindowCapturer()75 FakeWindowCapturer()
76 : callback_(NULL) {
77 }
~FakeWindowCapturer()78 virtual ~FakeWindowCapturer() {}
79
SetWindowList(const WindowList & list)80 void SetWindowList(const WindowList& list) {
81 base::AutoLock lock(window_list_lock_);
82 window_list_ = list;
83 }
84
85 // Sets |value| thats going to be used to memset() content of the frames
86 // generated for |window_id|. By default generated frames are set to zeros.
SetNextFrameValue(WindowId window_id,int8_t value)87 void SetNextFrameValue(WindowId window_id, int8_t value) {
88 base::AutoLock lock(frame_values_lock_);
89 frame_values_[window_id] = value;
90 }
91
92 // webrtc::WindowCapturer implementation.
Start(Callback * callback)93 virtual void Start(Callback* callback) OVERRIDE {
94 callback_ = callback;
95 }
96
Capture(const webrtc::DesktopRegion & region)97 virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE {
98 DCHECK(callback_);
99
100 base::AutoLock lock(frame_values_lock_);
101
102 std::map<WindowId, int8_t>::iterator it =
103 frame_values_.find(selected_window_id_);
104 int8_t value = (it != frame_values_.end()) ? it->second : 0;
105 webrtc::DesktopFrame* frame =
106 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10));
107 memset(frame->data(), value, frame->stride() * frame->size().height());
108 callback_->OnCaptureCompleted(frame);
109 }
110
GetWindowList(WindowList * windows)111 virtual bool GetWindowList(WindowList* windows) OVERRIDE {
112 base::AutoLock lock(window_list_lock_);
113 *windows = window_list_;
114 return true;
115 }
116
SelectWindow(WindowId id)117 virtual bool SelectWindow(WindowId id) OVERRIDE {
118 selected_window_id_ = id;
119 return true;
120 }
121
BringSelectedWindowToFront()122 virtual bool BringSelectedWindowToFront() OVERRIDE {
123 return true;
124 }
125
126 private:
127 Callback* callback_;
128 WindowList window_list_;
129 base::Lock window_list_lock_;
130
131 WindowId selected_window_id_;
132
133 // Frames to be captured per window.
134 std::map<WindowId, int8_t> frame_values_;
135 base::Lock frame_values_lock_;
136
137 DISALLOW_COPY_AND_ASSIGN(FakeWindowCapturer);
138 };
139
ACTION_P2(CheckListSize,model,expected_list_size)140 ACTION_P2(CheckListSize, model, expected_list_size) {
141 EXPECT_EQ(expected_list_size, model->GetSourceCount());
142 }
143
ACTION_P(QuitMessageLoop,message_loop)144 ACTION_P(QuitMessageLoop, message_loop) {
145 message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
146 }
147
148 class DesktopMediaListTest : public testing::Test {
149 public:
DesktopMediaListTest()150 DesktopMediaListTest()
151 : window_capturer_(NULL),
152 ui_thread_(content::BrowserThread::UI,
153 &message_loop_) {
154 }
155
CreateWithDefaultCapturers()156 void CreateWithDefaultCapturers() {
157 window_capturer_ = new FakeWindowCapturer();
158 model_.reset(new NativeDesktopMediaList(
159 scoped_ptr<webrtc::ScreenCapturer>(new FakeScreenCapturer()),
160 scoped_ptr<webrtc::WindowCapturer>(window_capturer_)));
161
162 // Set update period to reduce the time it takes to run tests.
163 model_->SetUpdatePeriod(base::TimeDelta::FromMilliseconds(0));
164 }
165
AddWindowsAndVerify(size_t count,bool window_only)166 webrtc::WindowCapturer::WindowList AddWindowsAndVerify(
167 size_t count, bool window_only) {
168 webrtc::WindowCapturer::WindowList list;
169 for (size_t i = 0; i < count; ++i) {
170 webrtc::WindowCapturer::Window window;
171 window.id = i + 1;
172 window.title = "Test window";
173 list.push_back(window);
174 }
175 window_capturer_->SetWindowList(list);
176
177 {
178 testing::InSequence dummy;
179 size_t source_count = window_only ? count : count + 1;
180 for (size_t i = 0; i < source_count; ++i) {
181 EXPECT_CALL(observer_, OnSourceAdded(i))
182 .WillOnce(CheckListSize(model_.get(), static_cast<int>(i + 1)));
183 }
184 for (size_t i = 0; i < source_count - 1; ++i) {
185 EXPECT_CALL(observer_, OnSourceThumbnailChanged(i));
186 }
187 EXPECT_CALL(observer_, OnSourceThumbnailChanged(source_count - 1))
188 .WillOnce(QuitMessageLoop(&message_loop_));
189 }
190 model_->StartUpdating(&observer_);
191 message_loop_.Run();
192
193 for (size_t i = 0; i < count; ++i) {
194 size_t source_index = window_only ? i : i + 1;
195 EXPECT_EQ(model_->GetSource(source_index).id.type,
196 content::DesktopMediaID::TYPE_WINDOW);
197 EXPECT_EQ(model_->GetSource(source_index).id.id, static_cast<int>(i + 1));
198 EXPECT_EQ(model_->GetSource(source_index).name,
199 base::UTF8ToUTF16("Test window"));
200 }
201 testing::Mock::VerifyAndClearExpectations(&observer_);
202 return list;
203 }
204
205 protected:
206 // Must be listed before |model_|, so it's destroyed last.
207 MockObserver observer_;
208
209 // Owned by |model_|;
210 FakeWindowCapturer* window_capturer_;
211
212 scoped_ptr<NativeDesktopMediaList> model_;
213
214 base::MessageLoop message_loop_;
215 content::TestBrowserThread ui_thread_;
216
217 DISALLOW_COPY_AND_ASSIGN(DesktopMediaListTest);
218 };
219
TEST_F(DesktopMediaListTest,InitialSourceList)220 TEST_F(DesktopMediaListTest, InitialSourceList) {
221 CreateWithDefaultCapturers();
222 webrtc::WindowCapturer::WindowList list = AddWindowsAndVerify(1, false);
223
224 EXPECT_EQ(model_->GetSource(0).id.type, content::DesktopMediaID::TYPE_SCREEN);
225 EXPECT_EQ(model_->GetSource(0).id.id, 0);
226 }
227
228 // Verifies that the window specified with SetViewDialogWindowId() is filtered
229 // from the results.
TEST_F(DesktopMediaListTest,Filtering)230 TEST_F(DesktopMediaListTest, Filtering) {
231 CreateWithDefaultCapturers();
232 webrtc::WindowCapturer::WindowList list = AddWindowsAndVerify(2, false);
233
234 EXPECT_EQ(model_->GetSource(0).id.type, content::DesktopMediaID::TYPE_SCREEN);
235 EXPECT_EQ(model_->GetSource(0).id.id, 0);
236 }
237
TEST_F(DesktopMediaListTest,WindowsOnly)238 TEST_F(DesktopMediaListTest, WindowsOnly) {
239 window_capturer_ = new FakeWindowCapturer();
240 model_.reset(new NativeDesktopMediaList(
241 scoped_ptr<webrtc::ScreenCapturer>(),
242 scoped_ptr<webrtc::WindowCapturer>(window_capturer_)));
243 AddWindowsAndVerify(1, true);
244 }
245
TEST_F(DesktopMediaListTest,ScreenOnly)246 TEST_F(DesktopMediaListTest, ScreenOnly) {
247 model_.reset(new NativeDesktopMediaList(
248 scoped_ptr<webrtc::ScreenCapturer>(new FakeScreenCapturer),
249 scoped_ptr<webrtc::WindowCapturer>()));
250
251 {
252 testing::InSequence dummy;
253 EXPECT_CALL(observer_, OnSourceAdded(0))
254 .WillOnce(CheckListSize(model_.get(), 1));
255 EXPECT_CALL(observer_, OnSourceThumbnailChanged(0))
256 .WillOnce(QuitMessageLoop(&message_loop_));
257 }
258 model_->StartUpdating(&observer_);
259
260 message_loop_.Run();
261
262 EXPECT_EQ(model_->GetSource(0).id.type, content::DesktopMediaID::TYPE_SCREEN);
263 }
264
TEST_F(DesktopMediaListTest,AddWindow)265 TEST_F(DesktopMediaListTest, AddWindow) {
266 CreateWithDefaultCapturers();
267 webrtc::WindowCapturer::WindowList list = AddWindowsAndVerify(1, false);
268
269 EXPECT_CALL(observer_, OnSourceAdded(2))
270 .WillOnce(DoAll(CheckListSize(model_.get(), 3),
271 QuitMessageLoop(&message_loop_)));
272
273 webrtc::WindowCapturer::Window window;
274 window.id = 0;
275 window.title = "Test window 0";
276 list.push_back(window);
277 window_capturer_->SetWindowList(list);
278
279 message_loop_.Run();
280
281 EXPECT_EQ(model_->GetSource(2).id.type, content::DesktopMediaID::TYPE_WINDOW);
282 EXPECT_EQ(model_->GetSource(2).id.id, 0);
283 }
284
TEST_F(DesktopMediaListTest,RemoveWindow)285 TEST_F(DesktopMediaListTest, RemoveWindow) {
286 CreateWithDefaultCapturers();
287 webrtc::WindowCapturer::WindowList list = AddWindowsAndVerify(2, false);
288
289 EXPECT_CALL(observer_, OnSourceRemoved(2))
290 .WillOnce(DoAll(CheckListSize(model_.get(), 2),
291 QuitMessageLoop(&message_loop_)));
292
293 list.erase(list.begin() + 1);
294 window_capturer_->SetWindowList(list);
295
296 message_loop_.Run();
297 }
298
TEST_F(DesktopMediaListTest,RemoveAllWindows)299 TEST_F(DesktopMediaListTest, RemoveAllWindows) {
300 CreateWithDefaultCapturers();
301 webrtc::WindowCapturer::WindowList list = AddWindowsAndVerify(2, false);
302
303 testing::InSequence seq;
304 EXPECT_CALL(observer_, OnSourceRemoved(1))
305 .WillOnce(CheckListSize(model_.get(), 2));
306 EXPECT_CALL(observer_, OnSourceRemoved(1))
307 .WillOnce(DoAll(CheckListSize(model_.get(), 1),
308 QuitMessageLoop(&message_loop_)));
309
310 list.erase(list.begin(), list.end());
311 window_capturer_->SetWindowList(list);
312
313 message_loop_.Run();
314 }
315
TEST_F(DesktopMediaListTest,UpdateTitle)316 TEST_F(DesktopMediaListTest, UpdateTitle) {
317 CreateWithDefaultCapturers();
318 webrtc::WindowCapturer::WindowList list = AddWindowsAndVerify(1, false);
319
320 EXPECT_CALL(observer_, OnSourceNameChanged(1))
321 .WillOnce(QuitMessageLoop(&message_loop_));
322
323 const std::string kTestTitle = "New Title";
324
325 list[0].title = kTestTitle;
326 window_capturer_->SetWindowList(list);
327
328 message_loop_.Run();
329
330 EXPECT_EQ(model_->GetSource(1).name, base::UTF8ToUTF16(kTestTitle));
331 }
332
TEST_F(DesktopMediaListTest,UpdateThumbnail)333 TEST_F(DesktopMediaListTest, UpdateThumbnail) {
334 CreateWithDefaultCapturers();
335 AddWindowsAndVerify(2, false);
336
337 EXPECT_CALL(observer_, OnSourceThumbnailChanged(1))
338 .WillOnce(QuitMessageLoop(&message_loop_));
339 // Update frame for the window and verify that we get notification about it.
340 window_capturer_->SetNextFrameValue(1, 1);
341
342 message_loop_.Run();
343 }
344
TEST_F(DesktopMediaListTest,MoveWindow)345 TEST_F(DesktopMediaListTest, MoveWindow) {
346 CreateWithDefaultCapturers();
347 webrtc::WindowCapturer::WindowList list = AddWindowsAndVerify(2, false);
348
349 EXPECT_CALL(observer_, OnSourceMoved(2, 1))
350 .WillOnce(DoAll(CheckListSize(model_.get(), 3),
351 QuitMessageLoop(&message_loop_)));
352
353 // Swap the two windows.
354 webrtc::WindowCapturer::Window temp = list[0];
355 list[0] = list[1];
356 list[1] = temp;
357 window_capturer_->SetWindowList(list);
358
359 message_loop_.Run();
360 }
361
362 } // namespace
363