| 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 |