summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkochi@chromium.org <kochi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-27 01:43:55 +0000
committerkochi@chromium.org <kochi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-27 01:43:55 +0000
commit6bdf093650147311383b38f0ad26d6d0f091b21f (patch)
treeeb3c753ed1cf40870ea67f74de03853807a9901c
parentf4726822d76caba76e080632ea2490c44065d86a (diff)
downloadchromium_src-6bdf093650147311383b38f0ad26d6d0f091b21f.zip
chromium_src-6bdf093650147311383b38f0ad26d6d0f091b21f.tar.gz
chromium_src-6bdf093650147311383b38f0ad26d6d0f091b21f.tar.bz2
Add Drive API parser for About/Apps json
BUG=chromium:127728 TEST=unit_test --gtest_filter="DriveApiParser.*" Review URL: https://chromiumcodereview.appspot.com/10810070 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148692 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/gdata/drive_api_parser.cc282
-rw-r--r--chrome/browser/chromeos/gdata/drive_api_parser.h262
-rw-r--r--chrome/browser/chromeos/gdata/drive_api_parser_unittest.cc158
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/test/data/chromeos/drive/about.json251
-rw-r--r--chrome/test/data/chromeos/drive/applist.json94
7 files changed, 1050 insertions, 0 deletions
diff --git a/chrome/browser/chromeos/gdata/drive_api_parser.cc b/chrome/browser/chromeos/gdata/drive_api_parser.cc
new file mode 100644
index 0000000..6f1cf75
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/drive_api_parser.cc
@@ -0,0 +1,282 @@
+// Copyright (c) 2012 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 "chrome/browser/chromeos/gdata/drive_api_parser.h"
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/json/json_value_converter.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/gdata/gdata_util.h"
+
+using base::Value;
+using base::DictionaryValue;
+using base::ListValue;
+
+namespace {
+
+// Converts |url_string| to |result|. Always returns true to be used
+// for JSONValueConverter::RegisterCustomField method.
+// TODO(mukai): make it return false in case of invalid |url_string|.
+bool GetGURLFromString(const base::StringPiece& url_string, GURL* result) {
+ *result = GURL(url_string.as_string());
+ return true;
+}
+
+// Drive v2 API JSON names.
+
+// Common
+const char kKind[] = "kind";
+
+// About Resource:
+const char kAboutKind[] = "drive#about";
+const char kRootFolderId[] = "rootFolderId";
+const char kQuotaBytesTotal[] = "quotaBytesTotal";
+const char kQuotaBytesUsed[] = "quotaBytesUsed";
+const char kLargestChangeId[] = "largestChangeId";
+
+// App Icon:
+const char kCategory[] = "category";
+const char kSize[] = "size";
+const char kIconUrl[] = "iconUrl";
+
+// Apps Resource:
+const char kAppKind[] = "drive#app";
+const char kId[] = "id";
+const char kETag[] = "etag";
+const char kName[] = "name";
+const char kObjectType[] = "objectType";
+const char kSupportsCreate[] = "supportsCreate";
+const char kSupportsImport[] = "supportsImport";
+const char kInstalled[] = "installed";
+const char kAuthorized[] = "authorized";
+const char kProductUrl[] = "productUrl";
+const char kPrimaryMimeTypes[] = "primaryMimeTypes";
+const char kSecondaryMimeTypes[] = "secondaryMimeTypes";
+const char kPrimaryFileExtensions[] = "primaryFileExtensions";
+const char kSecondaryFileExtensions[] = "secondaryFileExtensions";
+const char kIcons[] = "icons";
+
+// Apps List:
+const char kAppListKind[] = "drive#appList";
+const char kItems[] = "items";
+
+
+// Maps category name to enum IconCategory.
+struct AppIconCategoryMap {
+ gdata::DriveAppIcon::IconCategory category;
+ const char* category_name;
+};
+
+const AppIconCategoryMap kAppIconCategoryMap[] = {
+ { gdata::DriveAppIcon::DOCUMENT, "document" },
+ { gdata::DriveAppIcon::APPLICATION, "application" },
+ { gdata::DriveAppIcon::SHARED_DOCUMENT, "documentShared" },
+};
+
+// Checks if the JSON is expected kind. In Drive API, JSON data structure has
+// |kind| property which denotes the type of the structure (e.g. "drive#file").
+bool IsResourceKindExpected(const base::Value& value,
+ const std::string& expected_kind) {
+ const base::DictionaryValue* as_dict = NULL;
+ std::string kind;
+ return value.GetAsDictionary(&as_dict) &&
+ as_dict->HasKey(kKind) &&
+ as_dict->GetString(kKind, &kind) &&
+ kind == expected_kind;
+}
+
+} // namespace
+
+// TODO(kochi): Rename to namespace drive. http://crbug.com/136371
+namespace gdata {
+
+////////////////////////////////////////////////////////////////////////////////
+// AboutResource implementation
+
+AboutResource::AboutResource()
+ : quota_bytes_total_(0),
+ quota_bytes_used_(0),
+ largest_change_id_(0) {}
+
+AboutResource::~AboutResource() {}
+
+// static
+scoped_ptr<AboutResource> AboutResource::CreateFrom(const base::Value& value) {
+ scoped_ptr<AboutResource> resource(new AboutResource());
+ if (!IsResourceKindExpected(value, kAboutKind) || !resource->Parse(value)) {
+ LOG(ERROR) << "Unable to create: Invalid About resource JSON!";
+ return scoped_ptr<AboutResource>(NULL);
+ }
+ return resource.Pass();
+}
+
+// static
+void AboutResource::RegisterJSONConverter(
+ base::JSONValueConverter<AboutResource>* converter) {
+ converter->RegisterStringField(kRootFolderId,
+ &AboutResource::root_folder_id_);
+ converter->RegisterCustomField<int64>(kQuotaBytesTotal,
+ &AboutResource::quota_bytes_total_,
+ &base::StringToInt64);
+ converter->RegisterCustomField<int64>(kQuotaBytesUsed,
+ &AboutResource::quota_bytes_used_,
+ &base::StringToInt64);
+ converter->RegisterCustomField<int64>(kLargestChangeId,
+ &AboutResource::largest_change_id_,
+ &base::StringToInt64);
+}
+
+bool AboutResource::Parse(const base::Value& value) {
+ base::JSONValueConverter<AboutResource> converter;
+ if (!converter.Convert(value, this)) {
+ LOG(ERROR) << "Unable to parse: Invalid About resource JSON!";
+ return false;
+ }
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DriveAppIcon implementation
+
+DriveAppIcon::DriveAppIcon() {}
+
+DriveAppIcon::~DriveAppIcon() {}
+
+// static
+void DriveAppIcon::RegisterJSONConverter(
+ base::JSONValueConverter<DriveAppIcon>* converter) {
+ converter->RegisterCustomField<IconCategory>(
+ kCategory,
+ &DriveAppIcon::category_,
+ &DriveAppIcon::GetIconCategory);
+ converter->RegisterIntField(kSize, &DriveAppIcon::icon_side_length_);
+ converter->RegisterCustomField<GURL>(kIconUrl,
+ &DriveAppIcon::icon_url_,
+ GetGURLFromString);
+}
+
+// static
+scoped_ptr<DriveAppIcon> DriveAppIcon::CreateFrom(const base::Value& value) {
+ scoped_ptr<DriveAppIcon> resource(new DriveAppIcon());
+ if (!resource->Parse(value)) {
+ LOG(ERROR) << "Unable to create: Invalid DriveAppIcon JSON!";
+ return scoped_ptr<DriveAppIcon>(NULL);
+ }
+ return resource.Pass();
+}
+
+bool DriveAppIcon::Parse(const base::Value& value) {
+ base::JSONValueConverter<DriveAppIcon> converter;
+ if (!converter.Convert(value, this)) {
+ LOG(ERROR) << "Unable to parse: Invalid DriveAppIcon";
+ return false;
+ }
+ return true;
+}
+
+// static
+bool DriveAppIcon::GetIconCategory(const base::StringPiece& category,
+ DriveAppIcon::IconCategory* result) {
+ for (size_t i = 0; i < arraysize(kAppIconCategoryMap); i++) {
+ if (category == kAppIconCategoryMap[i].category_name) {
+ *result = kAppIconCategoryMap[i].category;
+ return true;
+ }
+ }
+ DVLOG(1) << "Unknown icon category " << category;
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AppResource implementation
+
+AppResource::AppResource() {}
+
+AppResource::~AppResource() {}
+
+// static
+void AppResource::RegisterJSONConverter(
+ base::JSONValueConverter<AppResource>* converter) {
+ converter->RegisterStringField(kId, &AppResource::id_);
+ converter->RegisterStringField(kName, &AppResource::name_);
+ converter->RegisterStringField(kObjectType, &AppResource::object_type_);
+ converter->RegisterBoolField(kSupportsCreate, &AppResource::supports_create_);
+ converter->RegisterBoolField(kSupportsImport, &AppResource::supports_import_);
+ converter->RegisterBoolField(kInstalled, &AppResource::installed_);
+ converter->RegisterBoolField(kAuthorized, &AppResource::authorized_);
+ converter->RegisterCustomField<GURL>(kProductUrl,
+ &AppResource::product_url_,
+ GetGURLFromString);
+ converter->RegisterRepeatedString(kPrimaryMimeTypes,
+ &AppResource::primary_mimetypes_);
+ converter->RegisterRepeatedString(kSecondaryMimeTypes,
+ &AppResource::secondary_mimetypes_);
+ converter->RegisterRepeatedString(kPrimaryFileExtensions,
+ &AppResource::primary_file_extensions_);
+ converter->RegisterRepeatedString(kSecondaryFileExtensions,
+ &AppResource::secondary_file_extensions_);
+ converter->RegisterRepeatedMessage(kIcons, &AppResource::icons_);
+}
+
+// static
+scoped_ptr<AppResource> AppResource::CreateFrom(const base::Value& value) {
+ scoped_ptr<AppResource> resource(new AppResource());
+ if (!IsResourceKindExpected(value, kAppKind) || !resource->Parse(value)) {
+ LOG(ERROR) << "Unable to create: Invalid AppResource JSON!";
+ return scoped_ptr<AppResource>(NULL);
+ }
+ return resource.Pass();
+}
+
+bool AppResource::Parse(const base::Value& value) {
+ base::JSONValueConverter<AppResource> converter;
+ if (!converter.Convert(value, this)) {
+ LOG(ERROR) << "Unable to parse: Invalid AppResource";
+ return false;
+ }
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AppList implementation
+
+AppList::AppList() {}
+
+AppList::~AppList() {}
+
+// static
+void AppList::RegisterJSONConverter(
+ base::JSONValueConverter<AppList>* converter) {
+ converter->RegisterStringField(kETag, &AppList::etag_);
+ converter->RegisterRepeatedMessage<AppResource>(kItems,
+ &AppList::items_);
+}
+
+// static
+scoped_ptr<AppList> AppList::CreateFrom(const base::Value& value) {
+ scoped_ptr<AppList> resource(new AppList());
+ if (!IsResourceKindExpected(value, kAppListKind) || !resource->Parse(value)) {
+ LOG(ERROR) << "Unable to create: Invalid AppList JSON!";
+ return scoped_ptr<AppList>(NULL);
+ }
+ return resource.Pass();
+}
+
+bool AppList::Parse(const base::Value& value) {
+ base::JSONValueConverter<AppList> converter;
+ if (!converter.Convert(value, this)) {
+ LOG(ERROR) << "Unable to parse: Invalid AppList";
+ return false;
+ }
+ return true;
+}
+
+} // namespace gdata
diff --git a/chrome/browser/chromeos/gdata/drive_api_parser.h b/chrome/browser/chromeos/gdata/drive_api_parser.h
new file mode 100644
index 0000000..097f4b8
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/drive_api_parser.h
@@ -0,0 +1,262 @@
+// Copyright (c) 2012 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_GDATA_DRIVE_API_PARSER_H_
+#define CHROME_BROWSER_CHROMEOS_GDATA_DRIVE_API_PARSER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/string_piece.h"
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+
+namespace base {
+class Value;
+template <class StructType>
+class JSONValueConverter;
+
+namespace internal {
+template <class NestedType>
+class RepeatedMessageConverter;
+} // namespace internal
+} // namespace base
+
+// TODO(kochi): Rename to namespace drive. http://crbug.com/136371
+namespace gdata {
+
+// About resource represents the account information about the current user.
+// https://developers.google.com/drive/v2/reference/about
+class AboutResource {
+ public:
+ ~AboutResource();
+
+ // Registers the mapping between JSON field names and the members in this
+ // class.
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<AboutResource>* converter);
+
+ // Creates about resource from parsed JSON.
+ static scoped_ptr<AboutResource> CreateFrom(const base::Value& value);
+
+ // Returns root folder ID.
+ const std::string& root_folder_id() const { return root_folder_id_; }
+ // Returns total number of quta bytes.
+ int64 quota_bytes_total() const { return quota_bytes_total_; }
+ // Returns the number of quota bytes used.
+ int64 quota_bytes_used() const { return quota_bytes_used_; }
+ // Returns the largest change ID number.
+ int64 largest_change_id() const { return largest_change_id_; }
+
+ private:
+ friend class DriveAPIParserTest;
+ FRIEND_TEST_ALL_PREFIXES(DriveAPIParserTest, AboutResourceParser);
+ AboutResource();
+
+ // Parses and initializes data members from content of |value|.
+ // Return false if parsing fails.
+ bool Parse(const base::Value& value);
+
+ std::string root_folder_id_;
+ int64 quota_bytes_total_;
+ int64 quota_bytes_used_;
+ int64 largest_change_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(AboutResource);
+};
+
+// DriveAppIcon represents an icon for Drive Application.
+// https://developers.google.com/drive/v2/reference/apps/list
+class DriveAppIcon {
+ public:
+ enum IconCategory {
+ UNKNOWN, // Uninitialized state
+ DOCUMENT, // Document icon for various MIME types
+ APPLICATION, // Application icon for various MIME types
+ SHARED_DOCUMENT, // Icon for documents that are shared from other users.
+ };
+
+ ~DriveAppIcon();
+
+ // Registers the mapping between JSON field names and the members in this
+ // class.
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<DriveAppIcon>* converter);
+
+ // Creates drive app icon instance from parsed JSON.
+ static scoped_ptr<DriveAppIcon> CreateFrom(const base::Value& value);
+
+ // Category of the icon.
+ IconCategory category() const { return category_; }
+
+ // Size in pixels of one side of the icon (icons are always square).
+ const int icon_side_length() const { return icon_side_length_; }
+
+ // Returns URL for this icon.
+ const GURL& icon_url() const { return icon_url_; }
+
+ private:
+ // Parses and initializes data members from content of |value|.
+ // Return false if parsing fails.
+ bool Parse(const base::Value& value);
+
+ // Extracts the icon category from the given string. Returns false and does
+ // not change |result| when |scheme| has an unrecognizable value.
+ static bool GetIconCategory(const base::StringPiece& category,
+ IconCategory* result);
+
+ friend class base::internal::RepeatedMessageConverter<DriveAppIcon>;
+ friend class AppResource;
+ DriveAppIcon();
+
+ IconCategory category_;
+ int icon_side_length_;
+ GURL icon_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveAppIcon);
+};
+
+// AppResource represents a Drive Application.
+// https://developers.google.com/drive/v2/reference/apps/list
+class AppResource {
+ public:
+ ~AppResource();
+
+ // Registers the mapping between JSON field names and the members in this
+ // class.
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<AppResource>* converter);
+
+ // Creates app resource from parsed JSON.
+ static scoped_ptr<AppResource> CreateFrom(const base::Value& value);
+
+ // Returns application ID, which is 12-digit decimals (e.g. "123456780123").
+ const std::string& id() const { return id_; }
+
+ // Returns application name.
+ const std::string& name() const { return name_; }
+
+ // Returns the name of the type of object this application creates.
+ // This can be any string. TODO(kochi): figure out how to use this value.
+ const std::string& object_type() const { return object_type_; }
+
+ // Returns whether this application suuports creating new objects.
+ bool supports_create() const { return supports_create_; }
+
+ // Returns whether this application supports importing Google Docs.
+ bool supports_import() const { return supports_import_; }
+
+ // Returns whether this application is installed.
+ bool is_installed() const { return installed_; }
+
+ // Returns whether this application is authorized to access data on the
+ // user's Drive.
+ bool is_authorized() const { return authorized_; }
+
+ // Returns the product URL, e.g. at Chrmoe Web Store.
+ const GURL& product_url() const { return product_url_; }
+
+ // List of primary mime types supported by this WebApp. Primary status should
+ // trigger this WebApp becoming the default handler of file instances that
+ // have these mime types.
+ const ScopedVector<std::string>& primary_mimetypes() const {
+ return primary_mimetypes_;
+ }
+
+ // List of secondary mime types supported by this WebApp. Secondary status
+ // should make this WebApp show up in "Open with..." pop-up menu of the
+ // default action menu for file with matching mime types.
+ const ScopedVector<std::string>& secondary_mimetypes() const {
+ return secondary_mimetypes_;
+ }
+
+ // List of primary file extensions supported by this WebApp. Primary status
+ // should trigger this WebApp becoming the default handler of file instances
+ // that match these extensions.
+ const ScopedVector<std::string>& primary_file_extensions() const {
+ return primary_file_extensions_;
+ }
+
+ // List of secondary file extensions supported by this WebApp. Secondary
+ // status should make this WebApp show up in "Open with..." pop-up menu of the
+ // default action menu for file with matching extensions.
+ const ScopedVector<std::string>& secondary_file_extensions() const {
+ return secondary_file_extensions_;
+ }
+
+ // Returns Icons for this application. An application can have multiple
+ // icons for different purpose (application, document, shared document)
+ // in several sizes.
+ const ScopedVector<DriveAppIcon>& icons() const {
+ return icons_;
+ }
+
+ private:
+ friend class base::internal::RepeatedMessageConverter<AppResource>;
+ friend class AppList;
+ AppResource();
+
+ // Parses and initializes data members from content of |value|.
+ // Return false if parsing fails.
+ bool Parse(const base::Value& value);
+
+ std::string id_;
+ std::string name_;
+ std::string object_type_;
+ bool supports_create_;
+ bool supports_import_;
+ bool installed_;
+ bool authorized_;
+ GURL product_url_;
+ ScopedVector<std::string> primary_mimetypes_;
+ ScopedVector<std::string> secondary_mimetypes_;
+ ScopedVector<std::string> primary_file_extensions_;
+ ScopedVector<std::string> secondary_file_extensions_;
+ ScopedVector<DriveAppIcon> icons_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppResource);
+};
+
+// AppList represents a list of Drive Applications.
+// https://developers.google.com/drive/v2/reference/apps/list
+class AppList {
+ public:
+ ~AppList();
+
+ // Registers the mapping between JSON field names and the members in this
+ // class.
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<AppList>* converter);
+
+ // Creates app list from parsed JSON.
+ static scoped_ptr<AppList> CreateFrom(const base::Value& value);
+
+ // ETag for this resource.
+ const std::string& etag() const { return etag_; }
+
+ // Returns a vector of applications.
+ const ScopedVector<AppResource>& items() const { return items_; }
+
+ private:
+ friend class DriveAPIParserTest;
+ FRIEND_TEST_ALL_PREFIXES(DriveAPIParserTest, AppListParser);
+ AppList();
+
+ // Parses and initializes data members from content of |value|.
+ // Return false if parsing fails.
+ bool Parse(const base::Value& value);
+
+ std::string etag_;
+ ScopedVector<AppResource> items_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppList);
+};
+
+} // namespace gdata
+
+#endif // CHROME_BROWSER_CHROMEOS_GDATA_DRIVE_API_PARSER_H_
diff --git a/chrome/browser/chromeos/gdata/drive_api_parser_unittest.cc b/chrome/browser/chromeos/gdata/drive_api_parser_unittest.cc
new file mode 100644
index 0000000..aafbcfe
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/drive_api_parser_unittest.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2012 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 "base/file_path.h"
+#include "base/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/path_service.h"
+#include "base/string16.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/gdata/drive_api_parser.h"
+#include "chrome/common/chrome_paths.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Value;
+using base::DictionaryValue;
+using base::ListValue;
+
+#define IF_EXPECT_EQ(arg1, arg2) \
+ EXPECT_EQ(arg1, arg2); \
+ if (arg1 == arg2)
+
+#define IF_EXPECT_TRUE(arg) \
+ EXPECT_TRUE(arg); \
+ if (arg)
+
+namespace gdata {
+
+class DriveAPIParserTest : public testing::Test {
+ protected:
+ static Value* LoadJSONFile(const std::string& filename) {
+ FilePath path;
+ std::string error;
+ // Test files for this unit test are located in
+ // src/chrome/test/data/chromeos/drive/*
+ PathService::Get(chrome::DIR_TEST_DATA, &path);
+ path = path.AppendASCII("chromeos")
+ .AppendASCII("drive")
+ .AppendASCII(filename.c_str());
+ EXPECT_TRUE(file_util::PathExists(path)) <<
+ "Couldn't find " << path.value();
+
+ JSONFileValueSerializer serializer(path);
+ Value* value = serializer.Deserialize(NULL, &error);
+ EXPECT_TRUE(value) << "Parse error " << path.value() << ": " << error;
+ return value;
+ }
+};
+
+// Test about resource parsing.
+TEST_F(DriveAPIParserTest, AboutResourceParser) {
+ std::string error;
+ scoped_ptr<Value> document(LoadJSONFile("about.json"));
+ ASSERT_TRUE(document.get());
+
+ ASSERT_EQ(Value::TYPE_DICTIONARY, document->GetType());
+ scoped_ptr<AboutResource> resource(new AboutResource());
+ EXPECT_TRUE(resource->Parse(*document));
+
+ EXPECT_EQ("0AIv7G8yEYAWHUk9123", resource->root_folder_id());
+ EXPECT_EQ(5368709120LL, resource->quota_bytes_total());
+ EXPECT_EQ(1073741824LL, resource->quota_bytes_used());
+ EXPECT_EQ(8177LL, resource->largest_change_id());
+}
+
+// Test app list parsing.
+TEST_F(DriveAPIParserTest, AppListParser) {
+ std::string error;
+ scoped_ptr<Value> document(LoadJSONFile("applist.json"));
+ ASSERT_TRUE(document.get());
+
+ ASSERT_EQ(Value::TYPE_DICTIONARY, document->GetType());
+ scoped_ptr<AppList> applist(new AppList);
+ EXPECT_TRUE(applist->Parse(*document));
+
+ EXPECT_EQ("\"Jm4BaSnCWNND-noZsHINRqj4ABC/tuqRBw0lvjUdPtc_2msA1tN4XYZ\"",
+ applist->etag());
+ IF_EXPECT_EQ(2U, applist->items().size()) {
+ // Check Drive app 1
+ const AppResource& app1 = *applist->items()[0];
+ EXPECT_EQ("123456788192", app1.id());
+ EXPECT_EQ("Drive app 1", app1.name());
+ EXPECT_EQ("", app1.object_type());
+ EXPECT_EQ(true, app1.supports_create());
+ EXPECT_EQ(true, app1.supports_import());
+ EXPECT_EQ(true, app1.is_installed());
+ EXPECT_EQ(false, app1.is_authorized());
+ EXPECT_EQ("https://chrome.google.com/webstore/detail/"
+ "abcdefghabcdefghabcdefghabcdefgh",
+ app1.product_url().spec());
+
+ IF_EXPECT_EQ(1U, app1.primary_mimetypes().size()) {
+ EXPECT_EQ("application/vnd.google-apps.drive-sdk.123456788192",
+ *app1.primary_mimetypes()[0]);
+ }
+
+ IF_EXPECT_EQ(2U, app1.secondary_mimetypes().size()) {
+ EXPECT_EQ("text/html", *app1.secondary_mimetypes()[0]);
+ EXPECT_EQ("text/plain", *app1.secondary_mimetypes()[1]);
+ }
+
+ IF_EXPECT_EQ(2U, app1.primary_file_extensions().size()) {
+ EXPECT_EQ("exe", *app1.primary_file_extensions()[0]);
+ EXPECT_EQ("com", *app1.primary_file_extensions()[1]);
+ }
+
+ EXPECT_EQ(0U, app1.secondary_file_extensions().size());
+
+ IF_EXPECT_EQ(6U, app1.icons().size()) {
+ const DriveAppIcon& icon1 = *app1.icons()[0];
+ EXPECT_EQ(DriveAppIcon::APPLICATION, icon1.category());
+ EXPECT_EQ(10, icon1.icon_side_length());
+ EXPECT_EQ("http://www.example.com/10.png", icon1.icon_url().spec());
+
+ const DriveAppIcon& icon6 = *app1.icons()[5];
+ EXPECT_EQ(DriveAppIcon::SHARED_DOCUMENT, icon6.category());
+ EXPECT_EQ(16, icon6.icon_side_length());
+ EXPECT_EQ("http://www.example.com/ds16.png", icon6.icon_url().spec());
+ }
+
+ // Check Drive app 2
+ const AppResource& app2 = *applist->items()[1];
+ EXPECT_EQ("876543210000", app2.id());
+ EXPECT_EQ("Drive app 2", app2.name());
+ EXPECT_EQ("", app2.object_type());
+ EXPECT_EQ(false, app2.supports_create());
+ EXPECT_EQ(false, app2.supports_import());
+ EXPECT_EQ(true, app2.is_installed());
+ EXPECT_EQ(false, app2.is_authorized());
+ EXPECT_EQ("https://chrome.google.com/webstore/detail/"
+ "hgfedcbahgfedcbahgfedcbahgfedcba",
+ app2.product_url().spec());
+
+ IF_EXPECT_EQ(3U, app2.primary_mimetypes().size()) {
+ EXPECT_EQ("image/jpeg",
+ *app2.primary_mimetypes()[0]);
+ EXPECT_EQ("image/png",
+ *app2.primary_mimetypes()[1]);
+ EXPECT_EQ("application/vnd.google-apps.drive-sdk.876543210000",
+ *app2.primary_mimetypes()[2]);
+ }
+
+ EXPECT_EQ(0U, app2.secondary_mimetypes().size());
+ EXPECT_EQ(0U, app2.primary_file_extensions().size());
+ EXPECT_EQ(0U, app2.secondary_file_extensions().size());
+
+ IF_EXPECT_EQ(3U, app2.icons().size()) {
+ const DriveAppIcon& icon2 = *app2.icons()[1];
+ EXPECT_EQ(DriveAppIcon::DOCUMENT, icon2.category());
+ EXPECT_EQ(10, icon2.icon_side_length());
+ EXPECT_EQ("http://www.example.com/d10.png", icon2.icon_url().spec());
+ }
+ }
+}
+
+} // namespace gdata
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index b1e1a98..cc22350 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -536,6 +536,8 @@
'browser/chromeos/external_metrics.h',
'browser/chromeos/external_protocol_dialog.cc',
'browser/chromeos/external_protocol_dialog.h',
+ 'browser/chromeos/gdata/drive_api_parser.cc',
+ 'browser/chromeos/gdata/drive_api_parser.h',
'browser/chromeos/gdata/drive_task_executor.cc',
'browser/chromeos/gdata/drive_task_executor.h',
'browser/chromeos/gdata/drive_webapps_registry.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 3211e67..42468e9 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1096,6 +1096,7 @@
'browser/chromeos/device_settings_provider_unittest.cc',
'browser/chromeos/extensions/file_browser_notifications_unittest.cc',
'browser/chromeos/external_metrics_unittest.cc',
+ 'browser/chromeos/gdata/drive_api_parser_unittest.cc',
'browser/chromeos/gdata/drive_webapps_registry_unittest.cc',
'browser/chromeos/gdata/gdata_cache_metadata_unittest.cc',
'browser/chromeos/gdata/gdata_cache_unittest.cc',
diff --git a/chrome/test/data/chromeos/drive/about.json b/chrome/test/data/chromeos/drive/about.json
new file mode 100644
index 0000000..c0f52ba
--- /dev/null
+++ b/chrome/test/data/chromeos/drive/about.json
@@ -0,0 +1,251 @@
+{
+ "kind": "drive#about",
+ "etag": "\"ia2FSHMEjvcFQvtI43H5NSXKABC/2QujpLrTbaz3UJ2wpt1HSuAZXYZ\"",
+ "selfLink": "https://www.googleapis.com/drive/v2/about",
+ "name": "Test User",
+ "quotaBytesTotal": "5368709120",
+ "quotaBytesUsed": "1073741824",
+ "quotaBytesUsedInTrash": "0",
+ "largestChangeId": "8177",
+ "rootFolderId": "0AIv7G8yEYAWHUk9123",
+ "domainSharingPolicy": "allowedWithWarning",
+ "permissionId": "12141426201712069789",
+ "importFormats": [
+ {
+ "source": "application/msword",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "text/plain",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "text/tab-separated-values",
+ "targets": [
+ "application/vnd.google-apps.spreadsheet"
+ ]
+ },
+ {
+ "source": "application/vnd.ms-excel",
+ "targets": [
+ "application/vnd.google-apps.spreadsheet"
+ ]
+ },
+ {
+ "source": "application/vnd.sun.xml.writer",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "image/bmp",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "application/rtf",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "image/gif",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "text/csv",
+ "targets": [
+ "application/vnd.google-apps.spreadsheet"
+ ]
+ },
+ {
+ "source": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ "targets": [
+ "application/vnd.google-apps.presentation"
+ ]
+ },
+ {
+ "source": "application/x-msmetafile",
+ "targets": [
+ "application/vnd.google-apps.drawing"
+ ]
+ },
+ {
+ "source": "application/vnd.oasis.opendocument.text",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ "targets": [
+ "application/vnd.google-apps.spreadsheet"
+ ]
+ },
+ {
+ "source": "application/vnd.ms-powerpoint",
+ "targets": [
+ "application/vnd.google-apps.presentation"
+ ]
+ },
+ {
+ "source": "application/x-vnd.oasis.opendocument.spreadsheet",
+ "targets": [
+ "application/vnd.google-apps.spreadsheet"
+ ]
+ },
+ {
+ "source": "application/pdf",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "image/jpeg",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "image/png",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ },
+ {
+ "source": "text/html",
+ "targets": [
+ "application/vnd.google-apps.document"
+ ]
+ }
+ ],
+ "exportFormats": [
+ {
+ "source": "application/vnd.google-apps.form",
+ "targets": [
+ "application/pdf",
+ "application/x-vnd.oasis.opendocument.spreadsheet",
+ "application/vnd.ms-excel"
+ ]
+ },
+ {
+ "source": "application/vnd.google-apps.presentation",
+ "targets": [
+ "application/pdf",
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation"
+ ]
+ },
+ {
+ "source": "application/vnd.google-apps.spreadsheet",
+ "targets": [
+ "application/pdf",
+ "application/x-vnd.oasis.opendocument.spreadsheet",
+ "application/vnd.ms-excel"
+ ]
+ },
+ {
+ "source": "application/vnd.google-apps.document",
+ "targets": [
+ "application/vnd.oasis.opendocument.text",
+ "application/msword",
+ "text/html",
+ "application/rtf",
+ "text/plain",
+ "application/pdf"
+ ]
+ },
+ {
+ "source": "application/vnd.google-apps.drawing",
+ "targets": [
+ "image/svg+xml",
+ "image/jpeg",
+ "image/png",
+ "application/pdf"
+ ]
+ }
+ ],
+ "additionalRoleInfo": [
+ {
+ "type": "application/vnd.google-apps.document",
+ "roleSets": [
+ {
+ "primaryRole": "reader",
+ "additionalRoles": [
+ "commenter"
+ ]
+ }
+ ]
+ },
+ {
+ "type": "application/vnd.google-apps.drawing",
+ "roleSets": [
+ {
+ "primaryRole": "reader",
+ "additionalRoles": [
+ "commenter"
+ ]
+ }
+ ]
+ },
+ {
+ "type": "application/vnd.google-apps.presentation",
+ "roleSets": [
+ {
+ "primaryRole": "reader",
+ "additionalRoles": [
+ "commenter"
+ ]
+ }
+ ]
+ }
+ ],
+ "features": [
+ {
+ "featureName": "ocr"
+ },
+ {
+ "featureName": "translation",
+ "featureRate": 2.0
+ }
+ ],
+ "maxUploadSizes": [
+ {
+ "type": "application/vnd.google-apps.drawing",
+ "size": "2097152"
+ },
+ {
+ "type": "*",
+ "size": "10737418240"
+ },
+ {
+ "type": "application/vnd.google-apps.presentation",
+ "size": "52428800"
+ },
+ {
+ "type": "application/vnd.google-apps.document",
+ "size": "2097152"
+ },
+ {
+ "type": "application/vnd.google-apps.spreadsheet",
+ "size": "20971520"
+ },
+ {
+ "type": "application/pdf",
+ "size": "10737418240"
+ }
+ ],
+ "isCurrentAppInstalled": false
+}
diff --git a/chrome/test/data/chromeos/drive/applist.json b/chrome/test/data/chromeos/drive/applist.json
new file mode 100644
index 0000000..5614b43
--- /dev/null
+++ b/chrome/test/data/chromeos/drive/applist.json
@@ -0,0 +1,94 @@
+{
+ "kind": "drive#appList",
+ "etag": "\"Jm4BaSnCWNND-noZsHINRqj4ABC/tuqRBw0lvjUdPtc_2msA1tN4XYZ\"",
+ "selfLink": "https://www.googleapis.com/drive/v2/apps",
+ "items": [
+ {
+ "kind": "drive#app",
+ "id": "123456788192",
+ "name": "Drive app 1",
+ "objectType": "",
+ "supportsCreate": true,
+ "supportsImport": true,
+ "installed": true,
+ "authorized": false,
+ "productUrl": "https://chrome.google.com/webstore/detail/abcdefghabcdefghabcdefghabcdefgh",
+ "primaryMimeTypes": [
+ "application/vnd.google-apps.drive-sdk.123456788192"
+ ],
+ "secondaryMimeTypes": [
+ "text/html",
+ "text/plain"
+ ],
+ "primaryFileExtensions": [
+ "exe",
+ "com"
+ ],
+ "icons": [
+ {
+ "category": "application",
+ "size": 10,
+ "iconUrl": "http://www.example.com/10.png"
+ },
+ {
+ "category": "application",
+ "size": 16,
+ "iconUrl": "http://www.example.com/16.png"
+ },
+ {
+ "category": "document",
+ "size": 10,
+ "iconUrl": "http://www.example.com/d10.png"
+ },
+ {
+ "category": "document",
+ "size": 16,
+ "iconUrl": "http://www.example.com/d16.png"
+ },
+ {
+ "category": "documentShared",
+ "size": 10,
+ "iconUrl": "http://www.example.com/ds10.png"
+ },
+ {
+ "category": "documentShared",
+ "size": 16,
+ "iconUrl": "http://www.example.com/ds16.png"
+ }
+ ]
+ },
+ {
+ "kind": "drive#app",
+ "id": "876543210000",
+ "name": "Drive app 2",
+ "objectType": "",
+ "supportsCreate": false,
+ "supportsImport": false,
+ "installed": true,
+ "authorized": false,
+ "productUrl": "https://chrome.google.com/webstore/detail/hgfedcbahgfedcbahgfedcbahgfedcba",
+ "primaryMimeTypes": [
+ "image/jpeg",
+ "image/png",
+ "application/vnd.google-apps.drive-sdk.876543210000"
+ ],
+ "icons": [
+ {
+ "category": "application",
+ "size": 10,
+ "iconUrl": "http://www.example.com/10.png"
+ },
+ {
+ "category": "document",
+ "size": 10,
+ "iconUrl": "http://www.example.com/d10.png"
+ },
+ {
+ "category": "documentShared",
+ "size": 10,
+ "iconUrl": "http://www.example.com/ds10.png"
+ }
+ ]
+ }
+ ]
+}