// 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 #include "base/bind.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/format_macros.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "chrome/browser/media_galleries/fileapi/iphoto_data_provider.h" #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" #include "chrome/browser/media_galleries/imported_media_gallery_registry.h" #include "chrome/test/base/in_process_browser_test.h" #include "content/public/browser/browser_thread.h" #include "url/gurl.h" using base::FilePath; namespace iphoto { class TestIPhotoDataProvider : public IPhotoDataProvider { public: TestIPhotoDataProvider(const base::FilePath& xml_library_path, const base::Closure& callback) : IPhotoDataProvider(xml_library_path), callback_(callback) { } ~TestIPhotoDataProvider() override {} private: void OnLibraryChanged(const base::FilePath& path, bool error) override { IPhotoDataProvider::OnLibraryChanged(path, error); callback_.Run(); } base::Closure callback_; DISALLOW_COPY_AND_ASSIGN(TestIPhotoDataProvider); }; class IPhotoDataProviderTest : public InProcessBrowserTest { public: IPhotoDataProviderTest() {} protected: void SetUp() override { ASSERT_TRUE(library_dir_.CreateUniqueTempDir()); WriteLibraryInternal(); // The ImportedMediaGalleryRegistry is created on which ever thread calls // GetInstance() first. It shouldn't matter what thread creates, however // in practice it is always created on the UI thread, so this calls // GetInstance here to mirror those real conditions. ImportedMediaGalleryRegistry::GetInstance(); InProcessBrowserTest::SetUp(); } void RunTest() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); base::RunLoop loop; quit_closure_ = loop.QuitClosure(); MediaFileSystemBackend::MediaTaskRunner()->PostTask( FROM_HERE, base::Bind(&IPhotoDataProviderTest::StartTestOnMediaTaskRunner, base::Unretained(this))); loop.Run(); } void WriteLibrary(const base::Closure& callback) { SetLibraryChangeCallback(callback); WriteLibraryInternal(); } void SetLibraryChangeCallback(const base::Closure& callback) { EXPECT_TRUE(library_changed_callback_.is_null()); library_changed_callback_ = callback; } IPhotoDataProvider* data_provider() const { return ImportedMediaGalleryRegistry::IPhotoDataProvider(); } const base::FilePath& library_dir() const { return library_dir_.path(); } base::FilePath XmlFile() const { return library_dir_.path().AppendASCII("library.xml"); } // Start the test. The data provider is refreshed before calling StartTest // and the result of the refresh is passed in. virtual void StartTest(bool parse_success) = 0; void TestDone() { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); ImportedMediaGalleryRegistry* imported_registry = ImportedMediaGalleryRegistry::GetInstance(); imported_registry->iphoto_data_provider_.reset(); content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, quit_closure_); } // Override to provide a full library string. virtual std::string GetLibraryString() { return "\n\n"; } private: void StartTestOnMediaTaskRunner() { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); ImportedMediaGalleryRegistry* imported_registry = ImportedMediaGalleryRegistry::GetInstance(); imported_registry->iphoto_data_provider_.reset( new TestIPhotoDataProvider( XmlFile(), base::Bind(&IPhotoDataProviderTest::OnLibraryChanged, base::Unretained(this)))); data_provider()->RefreshData(base::Bind(&IPhotoDataProviderTest::StartTest, base::Unretained(this))); }; void OnLibraryChanged() { DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread()); if (!library_changed_callback_.is_null()) { library_changed_callback_.Run(); library_changed_callback_.Reset(); } } void WriteLibraryInternal() { std::string xml = GetLibraryString(); ASSERT_EQ(static_cast(xml.size()), base::WriteFile(XmlFile(), xml.c_str(), xml.size())); } base::ScopedTempDir library_dir_; base::Closure library_changed_callback_; base::Closure quit_closure_; DISALLOW_COPY_AND_ASSIGN(IPhotoDataProviderTest); }; class IPhotoDataProviderBasicTest : public IPhotoDataProviderTest { public: IPhotoDataProviderBasicTest() {} std::string GetLibraryString() override { return "\n" "List of Albums\n" "" " \n" " AlbumId" " 14" " AlbumName" " Album1" " KeyList" " " " 1" " 3" // [3] and [4] are name dupes " 4" " " " \n" " \n" " AlbumId" " 15" " AlbumName" " Album2" " KeyList" " " " 2" " " " \n" " \n" " AlbumId" " 16" " AlbumName" " Album5" " KeyList" " " " 5" // A name dupe of [2], but in another album. " " " \n" "\n" "Master Image List\n" "\n" " 1\n" " \n" " MediaType" " Image" " Caption" " caption" " GUID\n" " guid1" " ImagePath" " /vol/path1.jpg" " ThumbPath" " /vol/thumb1.jpg" " \n" " 2\n" " \n" " MediaType" " Image" " Caption" " caption2" " GUID\n" " guid2" " ImagePath" " /vol/path2.jpg" " ThumbPath" " /vol/thumb2.jpg" " \n" " 3\n" " \n" " MediaType" " Image" " Caption" " caption3" " GUID\n" " guid3" " ImagePath" " /vol/path3.jpg" " ThumbPath" " /vol/thumb3.jpg" " \n" " 4\n" // A name duplicate of [3] in another path. " \n" " MediaType" " Image" " Caption" " caption" " GUID\n" " guid3" " ImagePath" " /vol/dupe/path3.jpg" " ThumbPath" " /vol/dupe/thumb3.jpg" " \n" " 5\n" // A name duplicate of [2] in another path. " \n" " MediaType" " Image" " Caption" " caption5" " GUID\n" " guid2" " ImagePath" " /vol/dupe/path2.jpg" " ThumbPath" " /vol/dupe/thumb2.jpg" " OriginalPath" \ " /original/vol/another2.jpg" \ " \n" "\n" "\n"; } void StartTest(bool parse_success) override { EXPECT_TRUE(parse_success); std::vector names = data_provider()->GetAlbumNames(); EXPECT_EQ(3U, names.size()); EXPECT_EQ("Album1", names[0]); EXPECT_EQ(FilePath("/vol/path1.jpg").value(), data_provider()->GetPhotoLocationInAlbum( "Album1", "path1.jpg").value()); EXPECT_EQ(FilePath("/vol/path3.jpg").value(), data_provider()->GetPhotoLocationInAlbum( "Album1", "path3.jpg").value()); EXPECT_EQ(FilePath("/vol/dupe/path3.jpg").value(), data_provider()->GetPhotoLocationInAlbum( "Album1", "path3(4).jpg").value()); EXPECT_EQ(FilePath().value(), data_provider()->GetPhotoLocationInAlbum( "Album1", "path5.jpg").value()); // path2.jpg is name-duped, but in different albums, and so should not // be mangled. EXPECT_EQ(FilePath("/vol/dupe/path2.jpg").value(), data_provider()->GetPhotoLocationInAlbum( "Album5", "path2.jpg").value()); EXPECT_EQ(FilePath("/vol/path2.jpg").value(), data_provider()->GetPhotoLocationInAlbum( "Album2", "path2.jpg").value()); std::map photos = data_provider()->GetAlbumContents("nonexistent"); EXPECT_EQ(0U, photos.size()); photos = data_provider()->GetAlbumContents("Album1"); EXPECT_EQ(3U, photos.size()); EXPECT_TRUE(ContainsKey(photos, "path1.jpg")); EXPECT_FALSE(ContainsKey(photos, "path2.jpg")); EXPECT_TRUE(ContainsKey(photos, "path3.jpg")); EXPECT_TRUE(ContainsKey(photos, "path3(4).jpg")); EXPECT_EQ(FilePath("/vol/path1.jpg").value(), photos["path1.jpg"].value()); EXPECT_EQ(FilePath("/vol/path3.jpg").value(), photos["path3.jpg"].value()); EXPECT_EQ(FilePath("/vol/dupe/path3.jpg").value(), photos["path3(4).jpg"].value()); photos = data_provider()->GetAlbumContents("Album2"); EXPECT_EQ(1U, photos.size()); EXPECT_TRUE(ContainsKey(photos, "path2.jpg")); EXPECT_FALSE(data_provider()->HasOriginals("Album1")); EXPECT_TRUE(data_provider()->HasOriginals("Album5")); std::map originals = data_provider()->GetOriginals("Album1"); EXPECT_EQ(0U, originals.size()); originals = data_provider()->GetOriginals("Album5"); EXPECT_EQ(1U, originals.size()); EXPECT_TRUE(ContainsKey(originals, "path2.jpg")); EXPECT_FALSE(ContainsKey(originals, "path1.jpg")); EXPECT_EQ(FilePath("/original/vol/another2.jpg").value(), originals["path2.jpg"].value()); base::FilePath original_path = data_provider()->GetOriginalPhotoLocation("Album5", "path2.jpg"); EXPECT_EQ(FilePath("/original/vol/another2.jpg").value(), original_path.value()); TestDone(); } private: DISALLOW_COPY_AND_ASSIGN(IPhotoDataProviderBasicTest); }; class IPhotoDataProviderRefreshTest : public IPhotoDataProviderTest { public: IPhotoDataProviderRefreshTest() {} std::string another_album; std::string GetLibraryString() override { return "\n" "List of Albums\n" "" " " " AlbumId" " 14" " AlbumName" " Album1" " KeyList" " " " 1" " " " \n" + another_album + "\n" "Master Image List\n" "\n" " 1\n" " \n" " MediaType" " Image" " Caption1" " caption" " GUID\n" " guid1" " ImagePath" " /vol/path1.jpg" " ThumbPath" " /vol/thumb1.jpg" " \n" "\n" "\n"; } void StartTest(bool parse_success) override { EXPECT_TRUE(parse_success); EXPECT_EQ(FilePath("/vol/path1.jpg"), data_provider()->GetPhotoLocationInAlbum("Album1", "path1.jpg")); std::vector names = data_provider()->GetAlbumNames(); EXPECT_EQ(1U, names.size()); EXPECT_EQ("Album1", names[0]); another_album = " " " AlbumId" " 14" " AlbumName" " Another Album" " KeyList" " " " 1" " " " \n"; WriteLibrary(base::Bind(&IPhotoDataProviderRefreshTest::CheckAfterWrite, base::Unretained(this))); } void CheckAfterWrite() { // No change -- data has not been parsed. EXPECT_EQ(FilePath("/vol/path1.jpg"), data_provider()->GetPhotoLocationInAlbum("Album1", "path1.jpg")); std::vector names = data_provider()->GetAlbumNames(); EXPECT_EQ(1U, names.size()); EXPECT_EQ("Album1", names[0]); data_provider()->RefreshData( base::Bind(&IPhotoDataProviderRefreshTest::CheckRefresh, base::Unretained(this))); } void CheckRefresh(bool is_valid) { EXPECT_TRUE(is_valid); EXPECT_EQ(FilePath("/vol/path1.jpg"), data_provider()->GetPhotoLocationInAlbum("Album1", "path1.jpg")); std::vector names = data_provider()->GetAlbumNames(); EXPECT_EQ(2U, names.size()); if (names.size() == 2U) { EXPECT_EQ("Album1", names[0]); EXPECT_EQ("Another Album", names[1]); } TestDone(); } private: DISALLOW_COPY_AND_ASSIGN(IPhotoDataProviderRefreshTest); }; class IPhotoDataProviderInvalidTest : public IPhotoDataProviderTest { public: IPhotoDataProviderInvalidTest() {} void StartTest(bool parse_success) override { EXPECT_TRUE(parse_success); SetLibraryChangeCallback( base::Bind(&IPhotoDataProvider::RefreshData, base::Unretained(data_provider()), base::Bind(&IPhotoDataProviderInvalidTest::CheckInvalid, base::Unretained(this)))); EXPECT_EQ(1L, base::WriteFile(XmlFile(), " ", 1)); } void CheckInvalid(bool is_valid) { EXPECT_FALSE(is_valid); TestDone(); } private: DISALLOW_COPY_AND_ASSIGN(IPhotoDataProviderInvalidTest); }; IN_PROC_BROWSER_TEST_F(IPhotoDataProviderBasicTest, BasicTest) { RunTest(); } IN_PROC_BROWSER_TEST_F(IPhotoDataProviderRefreshTest, RefreshTest) { RunTest(); } IN_PROC_BROWSER_TEST_F(IPhotoDataProviderInvalidTest, InvalidTest) { RunTest(); } } // namespace iphoto