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 <vector> 6 7#include "base/files/file_enumerator.h" 8#include "base/files/file_util.h" 9#include "base/files/scoped_temp_dir.h" 10#include "base/memory/ref_counted.h" 11#include "base/memory/scoped_ptr.h" 12#include "base/message_loop/message_loop.h" 13#include "base/run_loop.h" 14#include "build/build_config.h" 15#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" 16#include "chrome/browser/media_galleries/fileapi/picasa_data_provider.h" 17#include "chrome/common/media_galleries/picasa_test_util.h" 18#include "chrome/common/media_galleries/picasa_types.h" 19#include "chrome/test/base/in_process_browser_test.h" 20#include "content/public/test/test_browser_thread.h" 21 22namespace picasa { 23 24namespace { 25 26void VerifyTestAlbumTable(PicasaDataProvider* data_provider, 27 base::FilePath test_folder_1_path, 28 base::FilePath test_folder_2_path) { 29 scoped_ptr<AlbumMap> folders = data_provider->GetFolders(); 30 ASSERT_TRUE(folders.get()); 31 EXPECT_EQ(2u, folders->size()); 32 33 AlbumMap::const_iterator folder_1 = folders->find( 34 test_folder_1_path.BaseName().AsUTF8Unsafe() + " 1899-12-30"); 35 EXPECT_NE(folders->end(), folder_1); 36 EXPECT_EQ(test_folder_1_path.BaseName().AsUTF8Unsafe(), 37 folder_1->second.name); 38 EXPECT_EQ(test_folder_1_path, folder_1->second.path); 39 EXPECT_EQ("uid1", folder_1->second.uid); 40 41 AlbumMap::const_iterator folder_2 = folders->find( 42 test_folder_2_path.BaseName().AsUTF8Unsafe() + " 1899-12-30"); 43 EXPECT_NE(folders->end(), folder_2); 44 EXPECT_EQ(test_folder_2_path.BaseName().AsUTF8Unsafe(), 45 folder_2->second.name); 46 EXPECT_EQ(test_folder_2_path, folder_2->second.path); 47 EXPECT_EQ("uid4", folder_2->second.uid); 48 49 scoped_ptr<AlbumMap> albums = data_provider->GetAlbums(); 50 ASSERT_TRUE(albums.get()); 51 EXPECT_EQ(2u, albums->size()); 52 53 AlbumMap::const_iterator album_1 = albums->find("Album 1 Name 1899-12-30"); 54 EXPECT_NE(albums->end(), album_1); 55 EXPECT_EQ("Album 1 Name", album_1->second.name); 56 EXPECT_EQ(base::FilePath(), album_1->second.path); 57 EXPECT_EQ("uid3", album_1->second.uid); 58 59 AlbumMap::const_iterator album_2 = albums->find("Album 2 Name 1899-12-30"); 60 EXPECT_NE(albums->end(), album_2); 61 EXPECT_EQ("Album 2 Name", album_2->second.name); 62 EXPECT_EQ(base::FilePath(), album_2->second.path); 63 EXPECT_EQ("uid5", album_2->second.uid); 64} 65 66void VerifyTestAlbumsImagesIndex(PicasaDataProvider* data_provider, 67 base::FilePath test_folder_1_path, 68 base::FilePath test_folder_2_path) { 69 base::File::Error error; 70 scoped_ptr<AlbumImages> album_1_images = 71 data_provider->FindAlbumImages("uid3", &error); 72 ASSERT_TRUE(album_1_images); 73 EXPECT_EQ(base::File::FILE_OK, error); 74 EXPECT_EQ(2u, album_1_images->size()); 75 EXPECT_NE(album_1_images->end(), album_1_images->find("InBoth.jpg")); 76 EXPECT_EQ(test_folder_1_path.AppendASCII("InBoth.jpg"), 77 (*album_1_images)["InBoth.jpg"]); 78 EXPECT_NE(album_1_images->end(), 79 album_1_images->find("InFirstAlbumOnly.jpg")); 80 EXPECT_EQ(test_folder_2_path.AppendASCII("InFirstAlbumOnly.jpg"), 81 (*album_1_images)["InFirstAlbumOnly.jpg"]); 82 83 scoped_ptr<AlbumImages> album_2_images = 84 data_provider->FindAlbumImages("uid5", &error); 85 ASSERT_TRUE(album_2_images); 86 EXPECT_EQ(base::File::FILE_OK, error); 87 EXPECT_EQ(2u, album_2_images->size()); 88 EXPECT_NE(album_2_images->end(), album_2_images->find("InBoth.jpg")); 89 EXPECT_EQ(test_folder_1_path.AppendASCII("InBoth.jpg"), 90 (*album_2_images)["InBoth.jpg"]); 91 EXPECT_NE(album_2_images->end(), 92 album_2_images->find("InSecondAlbumOnly.jpg")); 93 EXPECT_EQ(test_folder_1_path.AppendASCII("InSecondAlbumOnly.jpg"), 94 (*album_2_images)["InSecondAlbumOnly.jpg"]); 95} 96 97} // namespace 98 99class TestPicasaDataProvider : public PicasaDataProvider { 100 public: 101 explicit TestPicasaDataProvider(const base::FilePath& database_path) 102 : PicasaDataProvider(database_path), 103 file_watch_request_returned_(false) { 104 } 105 106 virtual ~TestPicasaDataProvider() {} 107 108 // |ready_callback| called with true if and when the file watch is started 109 // successfully. If the file watch fails, it's called with false. 110 void EnsureFileWatchStartedForTesting(const ReadyCallback& ready_callback) { 111 if (!file_watch_request_returned_) { 112 file_watch_started_callbacks_.push_back(ready_callback); 113 return; 114 } 115 ready_callback.Run(temp_dir_watcher_.get() != NULL); 116 } 117 118 // Simulates the actual writing process of moving all the database files 119 // from the temporary directory to the database directory in a loop. 120 void MoveTempFilesToDatabase() { 121 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); 122 123 base::FileEnumerator file_enumerator( 124 database_path_.DirName().AppendASCII(kPicasaTempDirName), 125 false /* recursive */, 126 base::FileEnumerator::FILES); 127 128 for (base::FilePath src_path = file_enumerator.Next(); !src_path.empty(); 129 src_path = file_enumerator.Next()) { 130 ASSERT_TRUE( 131 base::Move(src_path, database_path_.Append(src_path.BaseName()))); 132 } 133 } 134 135 void SetInvalidateCallback(const base::Closure& callback) { 136 DCHECK(invalidate_callback_.is_null()); 137 invalidate_callback_ = callback; 138 } 139 140 virtual void InvalidateData() OVERRIDE { 141 PicasaDataProvider::InvalidateData(); 142 143 if (!invalidate_callback_.is_null()) { 144 invalidate_callback_.Run(); 145 invalidate_callback_.Reset(); 146 } 147 } 148 149 void SetAlbumMapsForTesting(const AlbumMap& album_map, 150 const AlbumMap& folder_map) { 151 album_map_ = album_map; 152 folder_map_ = folder_map; 153 } 154 155 private: 156 virtual void OnTempDirWatchStarted( 157 scoped_ptr<base::FilePathWatcher> temp_dir_watcher) OVERRIDE { 158 PicasaDataProvider::OnTempDirWatchStarted(temp_dir_watcher.Pass()); 159 160 file_watch_request_returned_ = true; 161 for (std::vector<ReadyCallback>::const_iterator it = 162 file_watch_started_callbacks_.begin(); 163 it != file_watch_started_callbacks_.end(); 164 ++it) { 165 it->Run(temp_dir_watcher_.get() != NULL); 166 } 167 file_watch_started_callbacks_.clear(); 168 } 169 170 // Used for test that utilizes file watch 171 bool file_watch_request_returned_; 172 std::vector<ReadyCallback> file_watch_started_callbacks_; 173 174 base::Closure invalidate_callback_; 175}; 176 177class PicasaDataProviderTest : public InProcessBrowserTest { 178 public: 179 PicasaDataProviderTest() {} 180 virtual ~PicasaDataProviderTest() {} 181 182 protected: 183 // Runs on the MediaTaskRunner and designed to be overridden by subclasses. 184 virtual void InitializeTestData() {} 185 186 void RunTest() { 187 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 188 base::RunLoop loop; 189 quit_closure_ = loop.QuitClosure(); 190 MediaFileSystemBackend::MediaTaskRunner()->PostTask( 191 FROM_HERE, 192 base::Bind(&PicasaDataProviderTest::SetupFoldersAndDataProvider, 193 base::Unretained(this))); 194 MediaFileSystemBackend::MediaTaskRunner()->PostTask( 195 FROM_HERE, 196 base::Bind(&PicasaDataProviderTest::InitializeTestData, 197 base::Unretained(this))); 198 MediaFileSystemBackend::MediaTaskRunner()->PostTask( 199 FROM_HERE, 200 base::Bind(&PicasaDataProviderTest::StartTestOnMediaTaskRunner, 201 base::Unretained(this))); 202 loop.Run(); 203 } 204 205 virtual PicasaDataProvider::DataType RequestedDataType() const = 0; 206 207 // Start the test. The data provider is refreshed before calling StartTest 208 // and the result of the refresh is passed in. 209 virtual void VerifyRefreshResults(bool parse_success) {}; 210 211 void TestDone() { 212 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); 213 214 // The data provider must be destructed on the MediaTaskRunner. This is done 215 // in a posted task rather than directly because TestDone is called by 216 // PicasaDataProvider. The callee should not destroy the caller. 217 MediaFileSystemBackend::MediaTaskRunner()->PostTask( 218 FROM_HERE, 219 base::Bind(&PicasaDataProviderTest::DestructDataProviderThenQuit, 220 base::Unretained(this))); 221 } 222 223 const base::FilePath& test_folder_1_path() { return test_folder_1_.path(); } 224 const base::FilePath& test_folder_2_path() { return test_folder_2_.path(); } 225 226 TestPicasaDataProvider* data_provider() const { 227 return picasa_data_provider_.get(); 228 } 229 230 const base::FilePath GetTempDirPath() const { 231 return picasa_root_dir_.path().AppendASCII(kPicasaTempDirName); 232 } 233 234 virtual base::FilePath GetColumnFileDestination() const { 235 return picasa_root_dir_.path().AppendASCII(kPicasaDatabaseDirName); 236 } 237 238 private: 239 void SetupFoldersAndDataProvider() { 240 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); 241 ASSERT_TRUE(test_folder_1_.CreateUniqueTempDir()); 242 ASSERT_TRUE(test_folder_2_.CreateUniqueTempDir()); 243 ASSERT_TRUE(picasa_root_dir_.CreateUniqueTempDir()); 244 ASSERT_TRUE(base::CreateDirectory( 245 picasa_root_dir_.path().AppendASCII(kPicasaDatabaseDirName))); 246 ASSERT_TRUE(base::CreateDirectory( 247 picasa_root_dir_.path().AppendASCII(kPicasaTempDirName))); 248 249 picasa_data_provider_.reset(new TestPicasaDataProvider( 250 picasa_root_dir_.path().AppendASCII(kPicasaDatabaseDirName))); 251 } 252 253 virtual void StartTestOnMediaTaskRunner() { 254 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); 255 256 data_provider()->RefreshData( 257 RequestedDataType(), 258 base::Bind(&PicasaDataProviderTest::VerifyRefreshResults, 259 base::Unretained(this))); 260 } 261 262 void DestructDataProviderThenQuit() { 263 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); 264 picasa_data_provider_.reset(); 265 content::BrowserThread::PostTask( 266 content::BrowserThread::UI, FROM_HERE, quit_closure_); 267 } 268 269 base::ScopedTempDir test_folder_1_; 270 base::ScopedTempDir test_folder_2_; 271 base::ScopedTempDir picasa_root_dir_; 272 273 scoped_ptr<TestPicasaDataProvider> picasa_data_provider_; 274 275 base::Closure quit_closure_; 276 277 DISALLOW_COPY_AND_ASSIGN(PicasaDataProviderTest); 278}; 279 280class PicasaDataProviderNoDatabaseGetListTest : public PicasaDataProviderTest { 281 protected: 282 virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { 283 return PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA; 284 } 285 virtual void VerifyRefreshResults(bool parse_success) OVERRIDE { 286 EXPECT_FALSE(parse_success); 287 TestDone(); 288 } 289}; 290 291IN_PROC_BROWSER_TEST_F(PicasaDataProviderNoDatabaseGetListTest, 292 NoDatabaseGetList) { 293 RunTest(); 294} 295 296class PicasaDataProviderNoDatabaseGetAlbumsImagesTest 297 : public PicasaDataProviderTest { 298 protected: 299 virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { 300 return PicasaDataProvider::ALBUMS_IMAGES_DATA; 301 } 302 virtual void VerifyRefreshResults(bool parse_success) OVERRIDE { 303 EXPECT_FALSE(parse_success); 304 TestDone(); 305 } 306}; 307 308IN_PROC_BROWSER_TEST_F(PicasaDataProviderNoDatabaseGetAlbumsImagesTest, 309 NoDatabaseGetAlbumsImages) { 310 RunTest(); 311} 312 313class PicasaDataProviderGetListTest : public PicasaDataProviderTest { 314 protected: 315 virtual void InitializeTestData() OVERRIDE { 316 WriteTestAlbumTable(GetColumnFileDestination(), test_folder_1_path(), 317 test_folder_2_path()); 318 } 319 320 virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { 321 return PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA; 322 } 323 324 virtual void VerifyRefreshResults(bool parse_success) OVERRIDE { 325 ASSERT_TRUE(parse_success); 326 VerifyTestAlbumTable( 327 data_provider(), test_folder_1_path(), test_folder_2_path()); 328 TestDone(); 329 } 330}; 331 332IN_PROC_BROWSER_TEST_F(PicasaDataProviderGetListTest, GetListTest) { 333 RunTest(); 334} 335 336class PicasaDataProviderGetAlbumsImagesTest : public PicasaDataProviderTest { 337 protected: 338 virtual void InitializeTestData() OVERRIDE { 339 WriteTestAlbumTable(GetColumnFileDestination(), test_folder_1_path(), 340 test_folder_2_path()); 341 WriteTestAlbumsImagesIndex(test_folder_1_path(), test_folder_2_path()); 342 } 343 344 virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { 345 return PicasaDataProvider::ALBUMS_IMAGES_DATA; 346 } 347 348 virtual void VerifyRefreshResults(bool parse_success) OVERRIDE { 349 ASSERT_TRUE(parse_success); 350 VerifyTestAlbumTable( 351 data_provider(), test_folder_1_path(), test_folder_2_path()); 352 VerifyTestAlbumsImagesIndex( 353 data_provider(), test_folder_1_path(), test_folder_2_path()); 354 TestDone(); 355 } 356}; 357 358IN_PROC_BROWSER_TEST_F(PicasaDataProviderGetAlbumsImagesTest, 359 GetAlbumsImagesTest) { 360 RunTest(); 361} 362 363class PicasaDataProviderMultipleMixedCallbacksTest 364 : public PicasaDataProviderTest { 365 public: 366 PicasaDataProviderMultipleMixedCallbacksTest() 367 : list_callbacks_called_(0), albums_images_callbacks_called_(0) {} 368 369 virtual void InitializeTestData() OVERRIDE { 370 WriteTestAlbumTable(GetColumnFileDestination(), test_folder_1_path(), 371 test_folder_2_path()); 372 WriteTestAlbumsImagesIndex(test_folder_1_path(), test_folder_2_path()); 373 } 374 375 virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { 376 return PicasaDataProvider::ALBUMS_IMAGES_DATA; 377 } 378 379 protected: 380 virtual void ListCallback(int expected_list_callbacks_called, 381 bool parse_success) { 382 ASSERT_TRUE(parse_success); 383 ASSERT_EQ(expected_list_callbacks_called, ++list_callbacks_called_); 384 VerifyTestAlbumTable( 385 data_provider(), test_folder_1_path(), test_folder_2_path()); 386 CheckTestDone(); 387 } 388 389 virtual void AlbumsImagesCallback(int expected_albums_images_callbacks_called, 390 bool parse_success) { 391 ASSERT_TRUE(parse_success); 392 ASSERT_EQ(expected_albums_images_callbacks_called, 393 ++albums_images_callbacks_called_); 394 VerifyTestAlbumsImagesIndex( 395 data_provider(), test_folder_1_path(), test_folder_2_path()); 396 CheckTestDone(); 397 } 398 399 private: 400 void CheckTestDone() { 401 ASSERT_LE(list_callbacks_called_, 2); 402 ASSERT_LE(albums_images_callbacks_called_, 2); 403 if (list_callbacks_called_ == 2 && albums_images_callbacks_called_ == 2) 404 TestDone(); 405 } 406 407 virtual void StartTestOnMediaTaskRunner() OVERRIDE { 408 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); 409 410 data_provider()->RefreshData( 411 PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA, 412 base::Bind(&PicasaDataProviderMultipleMixedCallbacksTest::ListCallback, 413 base::Unretained(this), 414 1)); 415 data_provider()->RefreshData( 416 PicasaDataProvider::ALBUMS_IMAGES_DATA, 417 base::Bind( 418 &PicasaDataProviderMultipleMixedCallbacksTest::AlbumsImagesCallback, 419 base::Unretained(this), 420 1)); 421 data_provider()->RefreshData( 422 PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA, 423 base::Bind(&PicasaDataProviderMultipleMixedCallbacksTest::ListCallback, 424 base::Unretained(this), 425 2)); 426 data_provider()->RefreshData( 427 PicasaDataProvider::ALBUMS_IMAGES_DATA, 428 base::Bind( 429 &PicasaDataProviderMultipleMixedCallbacksTest::AlbumsImagesCallback, 430 base::Unretained(this), 431 2)); 432 } 433 434 int list_callbacks_called_; 435 int albums_images_callbacks_called_; 436}; 437 438IN_PROC_BROWSER_TEST_F(PicasaDataProviderMultipleMixedCallbacksTest, 439 MultipleMixedCallbacks) { 440 RunTest(); 441} 442 443class PicasaDataProviderFileWatcherInvalidateTest 444 : public PicasaDataProviderGetListTest { 445 protected: 446 virtual void ListCallback(bool parse_success) { 447 ASSERT_FALSE(parse_success); 448 data_provider()->EnsureFileWatchStartedForTesting( 449 base::Bind(&PicasaDataProviderFileWatcherInvalidateTest:: 450 OnPicasaTempDirWatchStarted, 451 base::Unretained(this))); 452 } 453 454 void OnPicasaTempDirWatchStarted(bool file_watch_successful) { 455 ASSERT_TRUE(file_watch_successful); 456 457 // Validate the list after the file move triggers an invalidate. 458 data_provider()->SetInvalidateCallback(base::Bind( 459 &PicasaDataProvider::RefreshData, 460 base::Unretained(data_provider()), 461 RequestedDataType(), 462 base::Bind( 463 &PicasaDataProviderFileWatcherInvalidateTest::VerifyRefreshResults, 464 base::Unretained(this)))); 465 466 data_provider()->MoveTempFilesToDatabase(); 467 } 468 469 virtual base::FilePath GetColumnFileDestination() const OVERRIDE { 470 return GetTempDirPath(); 471 } 472 473 private: 474 virtual void StartTestOnMediaTaskRunner() OVERRIDE { 475 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); 476 477 // Refresh before moving album table to database dir, guaranteeing failure. 478 data_provider()->RefreshData( 479 RequestedDataType(), 480 base::Bind( 481 &PicasaDataProviderFileWatcherInvalidateTest::ListCallback, 482 base::Unretained(this))); 483 } 484}; 485 486IN_PROC_BROWSER_TEST_F(PicasaDataProviderFileWatcherInvalidateTest, 487 FileWatcherInvalidateTest) { 488 RunTest(); 489} 490 491class PicasaDataProviderInvalidateInflightTableReaderTest 492 : public PicasaDataProviderGetListTest { 493 protected: 494 // Don't write the database files until later. 495 virtual void InitializeTestData() OVERRIDE {} 496 497 private: 498 virtual void StartTestOnMediaTaskRunner() OVERRIDE { 499 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); 500 501 // Refresh before the database files have been written. 502 // This is guaranteed to fail to read the album table. 503 data_provider()->RefreshData( 504 RequestedDataType(), 505 base::Bind(&PicasaDataProviderInvalidateInflightTableReaderTest:: 506 VerifyRefreshResults, 507 base::Unretained(this))); 508 509 // Now write the album table and invalidate the inflight table reader. 510 PicasaDataProviderGetListTest::InitializeTestData(); 511 data_provider()->InvalidateData(); 512 513 // VerifyRefreshResults callback should receive correct results now. 514 } 515}; 516 517IN_PROC_BROWSER_TEST_F(PicasaDataProviderInvalidateInflightTableReaderTest, 518 InvalidateInflightTableReaderTest) { 519 RunTest(); 520} 521 522class PicasaDataProviderInvalidateInflightAlbumsIndexerTest 523 : public PicasaDataProviderGetAlbumsImagesTest { 524 protected: 525 virtual void ListCallback(bool parse_success) { 526 ASSERT_TRUE(parse_success); 527 528 // Empty the album maps to guarantee that the first utility process will 529 // fail to get the correct albums-images index. 530 data_provider()->SetAlbumMapsForTesting(AlbumMap(), AlbumMap()); 531 data_provider()->RefreshData( 532 PicasaDataProvider::ALBUMS_IMAGES_DATA, 533 base::Bind(&PicasaDataProviderInvalidateInflightAlbumsIndexerTest:: 534 VerifyRefreshResults, 535 base::Unretained(this))); 536 537 // Now invalidate all the data. The album maps will be re-read. 538 data_provider()->InvalidateData(); 539 540 // VerifyRefreshResults callback should receive correct results now. 541 } 542 543 private: 544 virtual void StartTestOnMediaTaskRunner() OVERRIDE { 545 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); 546 547 data_provider()->RefreshData( 548 PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA, 549 base::Bind(&PicasaDataProviderInvalidateInflightAlbumsIndexerTest:: 550 ListCallback, 551 base::Unretained(this))); 552 } 553}; 554 555IN_PROC_BROWSER_TEST_F(PicasaDataProviderInvalidateInflightAlbumsIndexerTest, 556 InvalidateInflightAlbumsIndexerTest) { 557 RunTest(); 558} 559 560} // namespace picasa 561