OLD | NEW |
| (Empty) |
1 // Copyright 2016 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/tab_desktop_media_list.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/files/file_util.h" | |
9 #include "base/location.h" | |
10 #include "base/run_loop.h" | |
11 #include "base/single_thread_task_runner.h" | |
12 #include "base/strings/utf_string_conversions.h" | |
13 #include "base/threading/thread_task_runner_handle.h" | |
14 #include "chrome/browser/media/desktop_media_list_observer.h" | |
15 #include "chrome/browser/profiles/profile_manager.h" | |
16 #include "chrome/browser/ui/browser_list.h" | |
17 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
18 #include "chrome/test/base/scoped_testing_local_state.h" | |
19 #include "chrome/test/base/test_browser_window.h" | |
20 #include "chrome/test/base/testing_browser_process.h" | |
21 #include "chrome/test/base/testing_profile.h" | |
22 #include "content/public/browser/favicon_status.h" | |
23 #include "content/public/browser/navigation_entry.h" | |
24 #include "content/public/browser/web_contents.h" | |
25 #include "content/public/common/content_switches.h" | |
26 #include "content/public/test/test_browser_thread_bundle.h" | |
27 #include "content/public/test/test_renderer_host.h" | |
28 #include "content/public/test/web_contents_tester.h" | |
29 #include "testing/gmock/include/gmock/gmock.h" | |
30 | |
31 #if defined(OS_CHROMEOS) | |
32 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h" | |
33 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h" | |
34 #include "chrome/browser/chromeos/settings/cros_settings.h" | |
35 #include "chrome/browser/chromeos/settings/device_settings_service.h" | |
36 #endif // defined(OS_CHROMEOS) | |
37 | |
38 using content::WebContents; | |
39 | |
40 namespace { | |
41 | |
42 static const int kDefaultSourceCount = 2; | |
43 static const int kThumbnailSize = 50; | |
44 | |
45 class UnittestProfileManager : public ::ProfileManagerWithoutInit { | |
46 public: | |
47 explicit UnittestProfileManager(const base::FilePath& user_data_dir) | |
48 : ::ProfileManagerWithoutInit(user_data_dir) {} | |
49 | |
50 protected: | |
51 Profile* CreateProfileHelper(const base::FilePath& file_path) override { | |
52 if (!base::PathExists(file_path)) { | |
53 if (!base::CreateDirectory(file_path)) | |
54 return NULL; | |
55 } | |
56 return new TestingProfile(file_path, NULL); | |
57 } | |
58 }; | |
59 | |
60 // Create a greyscale image with certain size and grayscale value. | |
61 gfx::Image CreateGrayscaleImage(gfx::Size size, uint8_t greyscale_value) { | |
62 SkBitmap result; | |
63 result.allocN32Pixels(size.width(), size.height(), true); | |
64 | |
65 result.lockPixels(); | |
66 uint8_t* pixels_data = reinterpret_cast<uint8_t*>(result.getPixels()); | |
67 | |
68 // Set greyscale value for all pixels. | |
69 for (int y = 0; y < result.height(); ++y) { | |
70 for (int x = 0; x < result.width(); ++x) { | |
71 pixels_data[result.rowBytes() * y + x * result.bytesPerPixel()] = | |
72 greyscale_value; | |
73 pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 1] = | |
74 greyscale_value; | |
75 pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 2] = | |
76 greyscale_value; | |
77 pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 3] = | |
78 0xff; | |
79 } | |
80 } | |
81 | |
82 result.unlockPixels(); | |
83 | |
84 return gfx::Image::CreateFrom1xBitmap(result); | |
85 } | |
86 | |
87 } // namespace | |
88 | |
89 class MockObserver : public DesktopMediaListObserver { | |
90 public: | |
91 MOCK_METHOD2(OnSourceAdded, void(DesktopMediaList* list, int index)); | |
92 MOCK_METHOD2(OnSourceRemoved, void(DesktopMediaList* list, int index)); | |
93 MOCK_METHOD3(OnSourceMoved, | |
94 void(DesktopMediaList* list, int old_index, int new_index)); | |
95 MOCK_METHOD2(OnSourceNameChanged, void(DesktopMediaList* list, int index)); | |
96 MOCK_METHOD2(OnSourceThumbnailChanged, | |
97 void(DesktopMediaList* list, int index)); | |
98 | |
99 void VerifyAndClearExpectations() { | |
100 testing::Mock::VerifyAndClearExpectations(this); | |
101 } | |
102 }; | |
103 | |
104 ACTION_P2(CheckListSize, list, expected_list_size) { | |
105 EXPECT_EQ(expected_list_size, list->GetSourceCount()); | |
106 } | |
107 | |
108 ACTION(QuitMessageLoop) { | |
109 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
110 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); | |
111 } | |
112 | |
113 class TabDesktopMediaListTest : public testing::Test { | |
114 protected: | |
115 TabDesktopMediaListTest() | |
116 : local_state_(TestingBrowserProcess::GetGlobal()) {} | |
117 | |
118 void AddWebcontents(int favicon_greyscale) { | |
119 TabStripModel* tab_strip_model = browser_->tab_strip_model(); | |
120 ASSERT_TRUE(tab_strip_model); | |
121 std::unique_ptr<WebContents> contents( | |
122 content::WebContentsTester::CreateTestWebContents( | |
123 profile_, content::SiteInstance::Create(profile_))); | |
124 ASSERT_TRUE(contents); | |
125 | |
126 contents->SetLastActiveTime(base::TimeTicks::Now()); | |
127 | |
128 // Get or create the transient NavigationEntry and add a title and a | |
129 // favicon to it. | |
130 content::NavigationEntry* entry = | |
131 contents->GetController().GetTransientEntry(); | |
132 if (!entry) { | |
133 std::unique_ptr<content::NavigationEntry> entry_new = | |
134 content::NavigationController::CreateNavigationEntry( | |
135 GURL("chrome://blank"), content::Referrer(), | |
136 ui::PAGE_TRANSITION_LINK, false, std::string(), profile_); | |
137 | |
138 contents->GetController().SetTransientEntry(std::move(entry_new)); | |
139 entry = contents->GetController().GetTransientEntry(); | |
140 } | |
141 | |
142 contents->UpdateTitleForEntry(entry, base::ASCIIToUTF16("Test tab")); | |
143 | |
144 content::FaviconStatus favicon_info; | |
145 favicon_info.image = | |
146 CreateGrayscaleImage(gfx::Size(10, 10), favicon_greyscale); | |
147 entry->GetFavicon() = favicon_info; | |
148 | |
149 contents_array_.push_back(std::move(contents)); | |
150 tab_strip_model->AppendWebContents(contents_array_.back().get(), false); | |
151 } | |
152 | |
153 void SetUp() override { | |
154 rvh_test_enabler_.reset(new content::RenderViewHostTestEnabler()); | |
155 // Create a new temporary directory, and store the path. | |
156 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
157 TestingBrowserProcess::GetGlobal()->SetProfileManager( | |
158 new UnittestProfileManager(temp_dir_.path())); | |
159 | |
160 #if defined(OS_CHROMEOS) | |
161 base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); | |
162 cl->AppendSwitch(switches::kTestType); | |
163 chromeos::WallpaperManager::Initialize(); | |
164 #endif | |
165 | |
166 // Create profile. | |
167 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
168 ASSERT_TRUE(profile_manager); | |
169 | |
170 profile_ = profile_manager->GetLastUsedProfileAllowedByPolicy(); | |
171 ASSERT_TRUE(profile_); | |
172 | |
173 // Create browser. | |
174 Browser::CreateParams profile_params(profile_); | |
175 browser_ = chrome::CreateBrowserWithTestWindowForParams(&profile_params); | |
176 ASSERT_TRUE(browser_); | |
177 for (int i = 0; i < kDefaultSourceCount; i++) { | |
178 AddWebcontents(i + 1); | |
179 } | |
180 } | |
181 | |
182 void TearDown() override { | |
183 for (auto& contents : contents_array_) | |
184 contents.reset(); | |
185 browser_.reset(); | |
186 TestingBrowserProcess::GetGlobal()->SetProfileManager(NULL); | |
187 base::RunLoop().RunUntilIdle(); | |
188 | |
189 #if defined(OS_CHROMEOS) | |
190 chromeos::WallpaperManager::Shutdown(); | |
191 #endif | |
192 rvh_test_enabler_.reset(); | |
193 } | |
194 | |
195 void CreateDefaultList() { | |
196 list_.reset(new TabDesktopMediaList()); | |
197 list_->SetThumbnailSize(gfx::Size(kThumbnailSize, kThumbnailSize)); | |
198 | |
199 // Set update period to reduce the time it takes to run tests. | |
200 // >0 to avoid unit test failure. | |
201 list_->SetUpdatePeriod(base::TimeDelta::FromMilliseconds(1)); | |
202 } | |
203 | |
204 void InitializeAndVerify() { | |
205 CreateDefaultList(); | |
206 | |
207 // The tabs in media source list are sorted in decreasing time order. The | |
208 // latest one is listed first. However, tabs are added to TabStripModel in | |
209 // increasing time order, the oldest one is added first. | |
210 { | |
211 testing::InSequence dummy; | |
212 | |
213 for (int i = 0; i < kDefaultSourceCount; i++) { | |
214 EXPECT_CALL(observer_, OnSourceAdded(list_.get(), i)) | |
215 .WillOnce(CheckListSize(list_.get(), i + 1)); | |
216 } | |
217 | |
218 for (int i = 0; i < kDefaultSourceCount - 1; i++) { | |
219 EXPECT_CALL(observer_, OnSourceThumbnailChanged( | |
220 list_.get(), kDefaultSourceCount - 1 - i)); | |
221 } | |
222 EXPECT_CALL(observer_, OnSourceThumbnailChanged(list_.get(), 0)) | |
223 .WillOnce(QuitMessageLoop()); | |
224 } | |
225 | |
226 list_->StartUpdating(&observer_); | |
227 base::RunLoop().Run(); | |
228 | |
229 for (int i = 0; i < kDefaultSourceCount; ++i) { | |
230 EXPECT_EQ(list_->GetSource(i).id.type, | |
231 content::DesktopMediaID::TYPE_WEB_CONTENTS); | |
232 } | |
233 | |
234 observer_.VerifyAndClearExpectations(); | |
235 } | |
236 | |
237 // The path to temporary directory used to contain the test operations. | |
238 base::ScopedTempDir temp_dir_; | |
239 ScopedTestingLocalState local_state_; | |
240 | |
241 std::unique_ptr<content::RenderViewHostTestEnabler> rvh_test_enabler_; | |
242 Profile* profile_; | |
243 std::unique_ptr<Browser> browser_; | |
244 std::vector<std::unique_ptr<WebContents>> contents_array_; | |
245 | |
246 // Must be listed before |list_|, so it's destroyed last. | |
247 MockObserver observer_; | |
248 std::unique_ptr<TabDesktopMediaList> list_; | |
249 | |
250 content::TestBrowserThreadBundle thread_bundle_; | |
251 | |
252 #if defined(OS_CHROMEOS) | |
253 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; | |
254 chromeos::ScopedTestCrosSettings test_cros_settings_; | |
255 chromeos::ScopedTestUserManager test_user_manager_; | |
256 #endif | |
257 | |
258 DISALLOW_COPY_AND_ASSIGN(TabDesktopMediaListTest); | |
259 }; | |
260 | |
261 TEST_F(TabDesktopMediaListTest, AddTab) { | |
262 InitializeAndVerify(); | |
263 | |
264 AddWebcontents(10); | |
265 | |
266 EXPECT_CALL(observer_, OnSourceAdded(list_.get(), 0)) | |
267 .WillOnce(CheckListSize(list_.get(), kDefaultSourceCount + 1)); | |
268 EXPECT_CALL(observer_, OnSourceThumbnailChanged(list_.get(), 0)) | |
269 .WillOnce(QuitMessageLoop()); | |
270 | |
271 base::RunLoop().Run(); | |
272 | |
273 list_.reset(); | |
274 } | |
275 | |
276 TEST_F(TabDesktopMediaListTest, RemoveTab) { | |
277 InitializeAndVerify(); | |
278 | |
279 TabStripModel* tab_strip_model = browser_->tab_strip_model(); | |
280 ASSERT_TRUE(tab_strip_model); | |
281 tab_strip_model->DetachWebContentsAt(kDefaultSourceCount - 1); | |
282 | |
283 EXPECT_CALL(observer_, OnSourceRemoved(list_.get(), 0)) | |
284 .WillOnce( | |
285 testing::DoAll(CheckListSize(list_.get(), kDefaultSourceCount - 1), | |
286 QuitMessageLoop())); | |
287 | |
288 base::RunLoop().Run(); | |
289 | |
290 list_.reset(); | |
291 } | |
292 | |
293 TEST_F(TabDesktopMediaListTest, MoveTab) { | |
294 InitializeAndVerify(); | |
295 | |
296 // Swap the two media sources by swap their time stamps. | |
297 TabStripModel* tab_strip_model = browser_->tab_strip_model(); | |
298 ASSERT_TRUE(tab_strip_model); | |
299 | |
300 WebContents* contents0 = tab_strip_model->GetWebContentsAt(0); | |
301 ASSERT_TRUE(contents0); | |
302 base::TimeTicks t0 = contents0->GetLastActiveTime(); | |
303 WebContents* contents1 = tab_strip_model->GetWebContentsAt(1); | |
304 ASSERT_TRUE(contents1); | |
305 base::TimeTicks t1 = contents1->GetLastActiveTime(); | |
306 | |
307 contents0->SetLastActiveTime(t1); | |
308 contents1->SetLastActiveTime(t0); | |
309 | |
310 EXPECT_CALL(observer_, OnSourceMoved(list_.get(), 1, 0)) | |
311 .WillOnce(testing::DoAll(CheckListSize(list_.get(), kDefaultSourceCount), | |
312 QuitMessageLoop())); | |
313 | |
314 base::RunLoop().Run(); | |
315 | |
316 list_.reset(); | |
317 } | |
318 | |
319 TEST_F(TabDesktopMediaListTest, UpdateTitle) { | |
320 InitializeAndVerify(); | |
321 | |
322 // Change tab's title. | |
323 TabStripModel* tab_strip_model = browser_->tab_strip_model(); | |
324 ASSERT_TRUE(tab_strip_model); | |
325 WebContents* contents = | |
326 tab_strip_model->GetWebContentsAt(kDefaultSourceCount - 1); | |
327 ASSERT_TRUE(contents); | |
328 const content::NavigationController& controller = contents->GetController(); | |
329 contents->UpdateTitleForEntry(controller.GetTransientEntry(), | |
330 base::ASCIIToUTF16("New test tab")); | |
331 | |
332 EXPECT_CALL(observer_, OnSourceNameChanged(list_.get(), 0)) | |
333 .WillOnce(QuitMessageLoop()); | |
334 | |
335 base::RunLoop().Run(); | |
336 | |
337 EXPECT_EQ(list_->GetSource(0).name, base::UTF8ToUTF16("New test tab")); | |
338 | |
339 list_.reset(); | |
340 } | |
341 | |
342 TEST_F(TabDesktopMediaListTest, UpdateThumbnail) { | |
343 InitializeAndVerify(); | |
344 | |
345 // Change tab's favicon. | |
346 TabStripModel* tab_strip_model = browser_->tab_strip_model(); | |
347 ASSERT_TRUE(tab_strip_model); | |
348 WebContents* contents = | |
349 tab_strip_model->GetWebContentsAt(kDefaultSourceCount - 1); | |
350 ASSERT_TRUE(contents); | |
351 | |
352 content::FaviconStatus favicon_info; | |
353 favicon_info.image = CreateGrayscaleImage(gfx::Size(10, 10), 100); | |
354 contents->GetController().GetTransientEntry()->GetFavicon() = favicon_info; | |
355 | |
356 EXPECT_CALL(observer_, OnSourceThumbnailChanged(list_.get(), 0)) | |
357 .WillOnce(QuitMessageLoop()); | |
358 | |
359 base::RunLoop().Run(); | |
360 | |
361 list_.reset(); | |
362 } | |
OLD | NEW |