root/chrome/browser/media_galleries/fileapi/iphoto_data_provider_browsertest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. callback_
  2. OnLibraryChanged
  3. SetUp
  4. RunTest
  5. WriteLibrary
  6. SetLibraryChangeCallback
  7. data_provider
  8. library_dir
  9. XmlFile
  10. TestDone
  11. GetLibraryString
  12. StartTestOnMediaTaskRunner
  13. OnLibraryChanged
  14. WriteLibraryInternal
  15. GetLibraryString
  16. StartTest
  17. GetLibraryString
  18. StartTest
  19. CheckAfterWrite
  20. CheckRefresh
  21. StartTest
  22. CheckInvalid
  23. IN_PROC_BROWSER_TEST_F
  24. IN_PROC_BROWSER_TEST_F
  25. IN_PROC_BROWSER_TEST_F

// 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 <string>
#include <vector>

#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/format_macros.h"
#include "base/logging.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) {
  }
  virtual ~TestIPhotoDataProvider() {}

 private:
  virtual 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() {}
  virtual ~IPhotoDataProviderTest() {}

 protected:
  virtual 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 "<plist><dict>\n</dict></plist>\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<int>(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() {}
  virtual ~IPhotoDataProviderBasicTest() {}

  virtual std::string GetLibraryString() OVERRIDE {
    return "<plist><dict>\n"
      "<key>List of Albums</key>\n"
      "<array>"
      "    <dict>\n"
      "      <key>AlbumId</key>"
      "      <integer>14</integer>"
      "      <key>AlbumName</key>"
      "      <string>Album1</string>"
      "      <key>KeyList</key>"
      "      <array>"
      "      <string>1</string>"
      "      <string>3</string>"  // [3] and [4] are name dupes
      "      <string>4</string>"
      "      </array>"
      "    </dict>\n"
      "    <dict>\n"
      "      <key>AlbumId</key>"
      "      <integer>15</integer>"
      "      <key>AlbumName</key>"
      "      <string>Album2</string>"
      "      <key>KeyList</key>"
      "      <array>"
      "      <string>2</string>"
      "      </array>"
      "    </dict>\n"
      "    <dict>\n"
      "      <key>AlbumId</key>"
      "      <integer>16</integer>"
      "      <key>AlbumName</key>"
      "      <string>Album5</string>"
      "      <key>KeyList</key>"
      "      <array>"
      "      <string>5</string>"  // A name dupe of [2], but in another album.
      "      </array>"
      "    </dict>\n"
      "</array>\n"
      "<key>Master Image List</key>\n"
      "<dict>\n"
      "  <key>1</key>\n"
      "  <dict>\n"
      "    <key>MediaType</key>"
      "    <string>Image</string>"
      "    <key>Caption</key>"
      "    <string>caption</string>"
      "    <key>GUID</key>\n"
      "    <string>guid1</string>"
      "    <key>ImagePath</key>"
      "    <string>/vol/path1.jpg</string>"
      "    <key>ThumbPath</key>"
      "    <string>/vol/thumb1.jpg</string>"
      "  </dict>\n"
      "  <key>2</key>\n"
      "  <dict>\n"
      "    <key>MediaType</key>"
      "    <string>Image</string>"
      "    <key>Caption</key>"
      "    <string>caption2</string>"
      "    <key>GUID</key>\n"
      "    <string>guid2</string>"
      "    <key>ImagePath</key>"
      "    <string>/vol/path2.jpg</string>"
      "    <key>ThumbPath</key>"
      "    <string>/vol/thumb2.jpg</string>"
      "  </dict>\n"
      "  <key>3</key>\n"
      "  <dict>\n"
      "    <key>MediaType</key>"
      "    <string>Image</string>"
      "    <key>Caption</key>"
      "    <string>caption3</string>"
      "    <key>GUID</key>\n"
      "    <string>guid3</string>"
      "    <key>ImagePath</key>"
      "    <string>/vol/path3.jpg</string>"
      "    <key>ThumbPath</key>"
      "    <string>/vol/thumb3.jpg</string>"
      "  </dict>\n"
      "  <key>4</key>\n"  // A name duplicate of [3] in another path.
      "  <dict>\n"
      "    <key>MediaType</key>"
      "    <string>Image</string>"
      "    <key>Caption</key>"
      "    <string>caption</string>"
      "    <key>GUID</key>\n"
      "    <string>guid3</string>"
      "    <key>ImagePath</key>"
      "    <string>/vol/dupe/path3.jpg</string>"
      "    <key>ThumbPath</key>"
      "    <string>/vol/dupe/thumb3.jpg</string>"
      "  </dict>\n"
      "  <key>5</key>\n"  // A name duplicate of [2] in another path.
      "  <dict>\n"
      "    <key>MediaType</key>"
      "    <string>Image</string>"
      "    <key>Caption</key>"
      "    <string>caption5</string>"
      "    <key>GUID</key>\n"
      "    <string>guid2</string>"
      "    <key>ImagePath</key>"
      "    <string>/vol/dupe/path2.jpg</string>"
      "    <key>ThumbPath</key>"
      "    <string>/vol/dupe/thumb2.jpg</string>"
      "    <key>OriginalPath</key>"             \
      "    <string>/original/vol/another2.jpg</string>"  \
      "  </dict>\n"
      "</dict>\n"
      "</dict></plist>\n";
  }

  virtual void StartTest(bool parse_success) OVERRIDE {
    EXPECT_TRUE(parse_success);

    std::vector<std::string> 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<std::string, base::FilePath> 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<std::string, base::FilePath> 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() {}
  virtual ~IPhotoDataProviderRefreshTest() {}

  std::string another_album;

  virtual std::string GetLibraryString() OVERRIDE {
    return "<plist><dict>\n"
      "<key>List of Albums</key>\n"
      "<array>"
      "    <dict>"
      "      <key>AlbumId</key>"
      "      <integer>14</integer>"
      "      <key>AlbumName</key>"
      "      <string>Album1</string>"
      "      <key>KeyList</key>"
      "      <array>"
      "      <string>1</string>"
      "      </array>"
      "    </dict>\n" +
      another_album +
      "</array>\n"
      "<key>Master Image List</key>\n"
      "<dict>\n"
      "  <key>1</key>\n"
      "  <dict>\n"
      "    <key>MediaType</key>"
      "    <string>Image</string>"
      "    <key>Caption1</key>"
      "    <string>caption</string>"
      "    <key>GUID</key>\n"
      "    <string>guid1</string>"
      "    <key>ImagePath</key>"
      "    <string>/vol/path1.jpg</string>"
      "    <key>ThumbPath</key>"
      "    <string>/vol/thumb1.jpg</string>"
      "  </dict>\n"
      "</dict>\n"
      "</dict></plist>\n";
  }

  virtual void StartTest(bool parse_success) OVERRIDE {
    EXPECT_TRUE(parse_success);

    EXPECT_EQ(FilePath("/vol/path1.jpg"),
              data_provider()->GetPhotoLocationInAlbum("Album1", "path1.jpg"));
    std::vector<std::string> names = data_provider()->GetAlbumNames();
    EXPECT_EQ(1U, names.size());
    EXPECT_EQ("Album1", names[0]);

    another_album =
      "    <dict>"
      "      <key>AlbumId</key>"
      "      <integer>14</integer>"
      "      <key>AlbumName</key>"
      "      <string>Another Album</string>"
      "      <key>KeyList</key>"
      "      <array>"
      "      <string>1</string>"
      "      </array>"
      "    </dict>\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<std::string> 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<std::string> 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() {}
  virtual ~IPhotoDataProviderInvalidTest() {}

  virtual 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

/* [<][>][^][v][top][bottom][index][help] */