// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "base/file_util.h" #include "base/files/file_enumerator.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "build/build_config.h" #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" #include "chrome/browser/media_galleries/fileapi/picasa_data_provider.h" #include "chrome/common/media_galleries/picasa_test_util.h" #include "chrome/common/media_galleries/picasa_types.h" #include "chrome/test/base/in_process_browser_test.h" #include "content/public/test/test_browser_thread.h" namespace picasa { namespace { void VerifyTestAlbumTable(PicasaDataProvider* data_provider, base::FilePath test_folder_1_path, base::FilePath test_folder_2_path) { scoped_ptr folders = data_provider->GetFolders(); ASSERT_TRUE(folders.get()); EXPECT_EQ(2u, folders->size()); AlbumMap::const_iterator folder_1 = folders->find( test_folder_1_path.BaseName().AsUTF8Unsafe() + " 1899-12-30"); EXPECT_NE(folders->end(), folder_1); EXPECT_EQ(test_folder_1_path.BaseName().AsUTF8Unsafe(), folder_1->second.name); EXPECT_EQ(test_folder_1_path, folder_1->second.path); EXPECT_EQ("uid1", folder_1->second.uid); AlbumMap::const_iterator folder_2 = folders->find( test_folder_2_path.BaseName().AsUTF8Unsafe() + " 1899-12-30"); EXPECT_NE(folders->end(), folder_2); EXPECT_EQ(test_folder_2_path.BaseName().AsUTF8Unsafe(), folder_2->second.name); EXPECT_EQ(test_folder_2_path, folder_2->second.path); EXPECT_EQ("uid4", folder_2->second.uid); scoped_ptr albums = data_provider->GetAlbums(); ASSERT_TRUE(albums.get()); EXPECT_EQ(2u, albums->size()); AlbumMap::const_iterator album_1 = albums->find("Album 1 Name 1899-12-30"); EXPECT_NE(albums->end(), album_1); EXPECT_EQ("Album 1 Name", album_1->second.name); EXPECT_EQ(base::FilePath(), album_1->second.path); EXPECT_EQ("uid3", album_1->second.uid); AlbumMap::const_iterator album_2 = albums->find("Album 2 Name 1899-12-30"); EXPECT_NE(albums->end(), album_2); EXPECT_EQ("Album 2 Name", album_2->second.name); EXPECT_EQ(base::FilePath(), album_2->second.path); EXPECT_EQ("uid5", album_2->second.uid); } void VerifyTestAlbumsImagesIndex(PicasaDataProvider* data_provider, base::FilePath test_folder_1_path, base::FilePath test_folder_2_path) { base::File::Error error; scoped_ptr album_1_images = data_provider->FindAlbumImages("uid3", &error); ASSERT_TRUE(album_1_images); EXPECT_EQ(base::File::FILE_OK, error); EXPECT_EQ(2u, album_1_images->size()); EXPECT_NE(album_1_images->end(), album_1_images->find("InBoth.jpg")); EXPECT_EQ(test_folder_1_path.AppendASCII("InBoth.jpg"), (*album_1_images)["InBoth.jpg"]); EXPECT_NE(album_1_images->end(), album_1_images->find("InFirstAlbumOnly.jpg")); EXPECT_EQ(test_folder_2_path.AppendASCII("InFirstAlbumOnly.jpg"), (*album_1_images)["InFirstAlbumOnly.jpg"]); scoped_ptr album_2_images = data_provider->FindAlbumImages("uid5", &error); ASSERT_TRUE(album_2_images); EXPECT_EQ(base::File::FILE_OK, error); EXPECT_EQ(2u, album_2_images->size()); EXPECT_NE(album_2_images->end(), album_2_images->find("InBoth.jpg")); EXPECT_EQ(test_folder_1_path.AppendASCII("InBoth.jpg"), (*album_2_images)["InBoth.jpg"]); EXPECT_NE(album_2_images->end(), album_2_images->find("InSecondAlbumOnly.jpg")); EXPECT_EQ(test_folder_1_path.AppendASCII("InSecondAlbumOnly.jpg"), (*album_2_images)["InSecondAlbumOnly.jpg"]); } } // namespace class TestPicasaDataProvider : public PicasaDataProvider { public: explicit TestPicasaDataProvider(const base::FilePath& database_path) : PicasaDataProvider(database_path), file_watch_request_returned_(false) { } virtual ~TestPicasaDataProvider() {} // |ready_callback| called with true if and when the file watch is started // successfully. If the file watch fails, it's called with false. void EnsureFileWatchStartedForTesting(const ReadyCallback& ready_callback) { if (!file_watch_request_returned_) { file_watch_started_callbacks_.push_back(ready_callback); return; } ready_callback.Run(temp_dir_watcher_.get() != NULL); } // Simulates the actual writing process of moving all the database files // from the temporary directory to the database directory in a loop. void MoveTempFilesToDatabase() { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); base::FileEnumerator file_enumerator( database_path_.DirName().AppendASCII(kPicasaTempDirName), false /* recursive */, base::FileEnumerator::FILES); for (base::FilePath src_path = file_enumerator.Next(); !src_path.empty(); src_path = file_enumerator.Next()) { ASSERT_TRUE( base::Move(src_path, database_path_.Append(src_path.BaseName()))); } } void SetInvalidateCallback(const base::Closure& callback) { DCHECK(invalidate_callback_.is_null()); invalidate_callback_ = callback; } virtual void InvalidateData() OVERRIDE { PicasaDataProvider::InvalidateData(); if (!invalidate_callback_.is_null()) { invalidate_callback_.Run(); invalidate_callback_.Reset(); } } void SetAlbumMapsForTesting(const AlbumMap& album_map, const AlbumMap& folder_map) { album_map_ = album_map; folder_map_ = folder_map; } private: virtual void OnTempDirWatchStarted( scoped_ptr temp_dir_watcher) OVERRIDE { PicasaDataProvider::OnTempDirWatchStarted(temp_dir_watcher.Pass()); file_watch_request_returned_ = true; for (std::vector::const_iterator it = file_watch_started_callbacks_.begin(); it != file_watch_started_callbacks_.end(); ++it) { it->Run(temp_dir_watcher_.get() != NULL); } file_watch_started_callbacks_.clear(); } // Used for test that utilizes file watch bool file_watch_request_returned_; std::vector file_watch_started_callbacks_; base::Closure invalidate_callback_; }; class PicasaDataProviderTest : public InProcessBrowserTest { public: PicasaDataProviderTest() {} virtual ~PicasaDataProviderTest() {} protected: // Runs on the MediaTaskRunner and designed to be overridden by subclasses. virtual void InitializeTestData() {} void RunTest() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); base::RunLoop loop; quit_closure_ = loop.QuitClosure(); MediaFileSystemBackend::MediaTaskRunner()->PostTask( FROM_HERE, base::Bind(&PicasaDataProviderTest::SetupFoldersAndDataProvider, base::Unretained(this))); MediaFileSystemBackend::MediaTaskRunner()->PostTask( FROM_HERE, base::Bind(&PicasaDataProviderTest::InitializeTestData, base::Unretained(this))); MediaFileSystemBackend::MediaTaskRunner()->PostTask( FROM_HERE, base::Bind(&PicasaDataProviderTest::StartTestOnMediaTaskRunner, base::Unretained(this))); loop.Run(); } virtual PicasaDataProvider::DataType RequestedDataType() const = 0; // Start the test. The data provider is refreshed before calling StartTest // and the result of the refresh is passed in. virtual void VerifyRefreshResults(bool parse_success) {}; void TestDone() { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); // The data provider must be destructed on the MediaTaskRunner. This is done // in a posted task rather than directly because TestDone is called by // PicasaDataProvider. The callee should not destroy the caller. MediaFileSystemBackend::MediaTaskRunner()->PostTask( FROM_HERE, base::Bind(&PicasaDataProviderTest::DestructDataProviderThenQuit, base::Unretained(this))); } const base::FilePath& test_folder_1_path() { return test_folder_1_.path(); } const base::FilePath& test_folder_2_path() { return test_folder_2_.path(); } TestPicasaDataProvider* data_provider() const { return picasa_data_provider_.get(); } const base::FilePath GetTempDirPath() const { return picasa_root_dir_.path().AppendASCII(kPicasaTempDirName); } virtual base::FilePath GetColumnFileDestination() const { return picasa_root_dir_.path().AppendASCII(kPicasaDatabaseDirName); } private: void SetupFoldersAndDataProvider() { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); ASSERT_TRUE(test_folder_1_.CreateUniqueTempDir()); ASSERT_TRUE(test_folder_2_.CreateUniqueTempDir()); ASSERT_TRUE(picasa_root_dir_.CreateUniqueTempDir()); ASSERT_TRUE(base::CreateDirectory( picasa_root_dir_.path().AppendASCII(kPicasaDatabaseDirName))); ASSERT_TRUE(base::CreateDirectory( picasa_root_dir_.path().AppendASCII(kPicasaTempDirName))); picasa_data_provider_.reset(new TestPicasaDataProvider( picasa_root_dir_.path().AppendASCII(kPicasaDatabaseDirName))); } virtual void StartTestOnMediaTaskRunner() { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); data_provider()->RefreshData( RequestedDataType(), base::Bind(&PicasaDataProviderTest::VerifyRefreshResults, base::Unretained(this))); } void DestructDataProviderThenQuit() { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); picasa_data_provider_.reset(); content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, quit_closure_); } base::ScopedTempDir test_folder_1_; base::ScopedTempDir test_folder_2_; base::ScopedTempDir picasa_root_dir_; scoped_ptr picasa_data_provider_; base::Closure quit_closure_; DISALLOW_COPY_AND_ASSIGN(PicasaDataProviderTest); }; class PicasaDataProviderNoDatabaseGetListTest : public PicasaDataProviderTest { protected: virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { return PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA; } virtual void VerifyRefreshResults(bool parse_success) OVERRIDE { EXPECT_FALSE(parse_success); TestDone(); } }; IN_PROC_BROWSER_TEST_F(PicasaDataProviderNoDatabaseGetListTest, NoDatabaseGetList) { RunTest(); } class PicasaDataProviderNoDatabaseGetAlbumsImagesTest : public PicasaDataProviderTest { protected: virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { return PicasaDataProvider::ALBUMS_IMAGES_DATA; } virtual void VerifyRefreshResults(bool parse_success) OVERRIDE { EXPECT_FALSE(parse_success); TestDone(); } }; IN_PROC_BROWSER_TEST_F(PicasaDataProviderNoDatabaseGetAlbumsImagesTest, NoDatabaseGetAlbumsImages) { RunTest(); } class PicasaDataProviderGetListTest : public PicasaDataProviderTest { protected: virtual void InitializeTestData() OVERRIDE { WriteTestAlbumTable(GetColumnFileDestination(), test_folder_1_path(), test_folder_2_path()); } virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { return PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA; } virtual void VerifyRefreshResults(bool parse_success) OVERRIDE { ASSERT_TRUE(parse_success); VerifyTestAlbumTable( data_provider(), test_folder_1_path(), test_folder_2_path()); TestDone(); } }; IN_PROC_BROWSER_TEST_F(PicasaDataProviderGetListTest, GetListTest) { RunTest(); } class PicasaDataProviderGetAlbumsImagesTest : public PicasaDataProviderTest { protected: virtual void InitializeTestData() OVERRIDE { WriteTestAlbumTable(GetColumnFileDestination(), test_folder_1_path(), test_folder_2_path()); WriteTestAlbumsImagesIndex(test_folder_1_path(), test_folder_2_path()); } virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { return PicasaDataProvider::ALBUMS_IMAGES_DATA; } virtual void VerifyRefreshResults(bool parse_success) OVERRIDE { ASSERT_TRUE(parse_success); VerifyTestAlbumTable( data_provider(), test_folder_1_path(), test_folder_2_path()); VerifyTestAlbumsImagesIndex( data_provider(), test_folder_1_path(), test_folder_2_path()); TestDone(); } }; IN_PROC_BROWSER_TEST_F(PicasaDataProviderGetAlbumsImagesTest, GetAlbumsImagesTest) { RunTest(); } class PicasaDataProviderMultipleMixedCallbacksTest : public PicasaDataProviderTest { public: PicasaDataProviderMultipleMixedCallbacksTest() : list_callbacks_called_(0), albums_images_callbacks_called_(0) {} virtual void InitializeTestData() OVERRIDE { WriteTestAlbumTable(GetColumnFileDestination(), test_folder_1_path(), test_folder_2_path()); WriteTestAlbumsImagesIndex(test_folder_1_path(), test_folder_2_path()); } virtual PicasaDataProvider::DataType RequestedDataType() const OVERRIDE { return PicasaDataProvider::ALBUMS_IMAGES_DATA; } protected: virtual void ListCallback(int expected_list_callbacks_called, bool parse_success) { ASSERT_TRUE(parse_success); ASSERT_EQ(expected_list_callbacks_called, ++list_callbacks_called_); VerifyTestAlbumTable( data_provider(), test_folder_1_path(), test_folder_2_path()); CheckTestDone(); } virtual void AlbumsImagesCallback(int expected_albums_images_callbacks_called, bool parse_success) { ASSERT_TRUE(parse_success); ASSERT_EQ(expected_albums_images_callbacks_called, ++albums_images_callbacks_called_); VerifyTestAlbumsImagesIndex( data_provider(), test_folder_1_path(), test_folder_2_path()); CheckTestDone(); } private: void CheckTestDone() { ASSERT_LE(list_callbacks_called_, 2); ASSERT_LE(albums_images_callbacks_called_, 2); if (list_callbacks_called_ == 2 && albums_images_callbacks_called_ == 2) TestDone(); } virtual void StartTestOnMediaTaskRunner() OVERRIDE { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); data_provider()->RefreshData( PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA, base::Bind(&PicasaDataProviderMultipleMixedCallbacksTest::ListCallback, base::Unretained(this), 1)); data_provider()->RefreshData( PicasaDataProvider::ALBUMS_IMAGES_DATA, base::Bind( &PicasaDataProviderMultipleMixedCallbacksTest::AlbumsImagesCallback, base::Unretained(this), 1)); data_provider()->RefreshData( PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA, base::Bind(&PicasaDataProviderMultipleMixedCallbacksTest::ListCallback, base::Unretained(this), 2)); data_provider()->RefreshData( PicasaDataProvider::ALBUMS_IMAGES_DATA, base::Bind( &PicasaDataProviderMultipleMixedCallbacksTest::AlbumsImagesCallback, base::Unretained(this), 2)); } int list_callbacks_called_; int albums_images_callbacks_called_; }; IN_PROC_BROWSER_TEST_F(PicasaDataProviderMultipleMixedCallbacksTest, MultipleMixedCallbacks) { RunTest(); } class PicasaDataProviderFileWatcherInvalidateTest : public PicasaDataProviderGetListTest { protected: virtual void ListCallback(bool parse_success) { ASSERT_FALSE(parse_success); data_provider()->EnsureFileWatchStartedForTesting( base::Bind(&PicasaDataProviderFileWatcherInvalidateTest:: OnPicasaTempDirWatchStarted, base::Unretained(this))); } void OnPicasaTempDirWatchStarted(bool file_watch_successful) { ASSERT_TRUE(file_watch_successful); // Validate the list after the file move triggers an invalidate. data_provider()->SetInvalidateCallback(base::Bind( &PicasaDataProvider::RefreshData, base::Unretained(data_provider()), RequestedDataType(), base::Bind( &PicasaDataProviderFileWatcherInvalidateTest::VerifyRefreshResults, base::Unretained(this)))); data_provider()->MoveTempFilesToDatabase(); } virtual base::FilePath GetColumnFileDestination() const OVERRIDE { return GetTempDirPath(); } private: virtual void StartTestOnMediaTaskRunner() OVERRIDE { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); // Refresh before moving album table to database dir, guaranteeing failure. data_provider()->RefreshData( RequestedDataType(), base::Bind( &PicasaDataProviderFileWatcherInvalidateTest::ListCallback, base::Unretained(this))); } }; IN_PROC_BROWSER_TEST_F(PicasaDataProviderFileWatcherInvalidateTest, FileWatcherInvalidateTest) { RunTest(); } class PicasaDataProviderInvalidateInflightTableReaderTest : public PicasaDataProviderGetListTest { protected: // Don't write the database files until later. virtual void InitializeTestData() OVERRIDE {} private: virtual void StartTestOnMediaTaskRunner() OVERRIDE { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); // Refresh before the database files have been written. // This is guaranteed to fail to read the album table. data_provider()->RefreshData( RequestedDataType(), base::Bind(&PicasaDataProviderInvalidateInflightTableReaderTest:: VerifyRefreshResults, base::Unretained(this))); // Now write the album table and invalidate the inflight table reader. PicasaDataProviderGetListTest::InitializeTestData(); data_provider()->InvalidateData(); // VerifyRefreshResults callback should receive correct results now. } }; IN_PROC_BROWSER_TEST_F(PicasaDataProviderInvalidateInflightTableReaderTest, InvalidateInflightTableReaderTest) { RunTest(); } class PicasaDataProviderInvalidateInflightAlbumsIndexerTest : public PicasaDataProviderGetAlbumsImagesTest { protected: virtual void ListCallback(bool parse_success) { ASSERT_TRUE(parse_success); // Empty the album maps to guarantee that the first utility process will // fail to get the correct albums-images index. data_provider()->SetAlbumMapsForTesting(AlbumMap(), AlbumMap()); data_provider()->RefreshData( PicasaDataProvider::ALBUMS_IMAGES_DATA, base::Bind(&PicasaDataProviderInvalidateInflightAlbumsIndexerTest:: VerifyRefreshResults, base::Unretained(this))); // Now invalidate all the data. The album maps will be re-read. data_provider()->InvalidateData(); // VerifyRefreshResults callback should receive correct results now. } private: virtual void StartTestOnMediaTaskRunner() OVERRIDE { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); data_provider()->RefreshData( PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA, base::Bind(&PicasaDataProviderInvalidateInflightAlbumsIndexerTest:: ListCallback, base::Unretained(this))); } }; IN_PROC_BROWSER_TEST_F(PicasaDataProviderInvalidateInflightAlbumsIndexerTest, InvalidateInflightAlbumsIndexerTest) { RunTest(); } } // namespace picasa