1// Copyright 2014 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 "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
6
7#include <string>
8
9#include "base/files/file.h"
10#include "base/files/file_path.h"
11#include "base/json/json_reader.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/memory/scoped_vector.h"
14#include "base/values.h"
15#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
16#include "chrome/common/extensions/api/file_system_provider.h"
17#include "chrome/common/extensions/api/file_system_provider_internal.h"
18#include "extensions/browser/event_router.h"
19#include "storage/browser/fileapi/async_file_util.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22namespace chromeos {
23namespace file_system_provider {
24namespace operations {
25namespace {
26
27const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
28const char kFileSystemId[] = "testing-file-system";
29const char kMimeType[] = "text/plain";
30const int kRequestId = 2;
31const base::FilePath::CharType kDirectoryPath[] = "/directory";
32
33// URLs are case insensitive, so it should pass the sanity check.
34const char kThumbnail[] = "DaTa:ImAgE/pNg;base64,";
35
36// Callback invocation logger. Acts as a fileapi end-point.
37class CallbackLogger {
38 public:
39  class Event {
40   public:
41    Event(scoped_ptr<EntryMetadata> metadata, base::File::Error result)
42        : metadata_(metadata.Pass()), result_(result) {}
43    virtual ~Event() {}
44
45    const EntryMetadata* metadata() const { return metadata_.get(); }
46    base::File::Error result() const { return result_; }
47
48   private:
49    scoped_ptr<EntryMetadata> metadata_;
50    base::File::Error result_;
51
52    DISALLOW_COPY_AND_ASSIGN(Event);
53  };
54
55  CallbackLogger() {}
56  virtual ~CallbackLogger() {}
57
58  void OnGetMetadata(scoped_ptr<EntryMetadata> metadata,
59                     base::File::Error result) {
60    events_.push_back(new Event(metadata.Pass(), result));
61  }
62
63  const ScopedVector<Event>& events() const { return events_; }
64
65 private:
66  ScopedVector<Event> events_;
67  bool dispatch_reply_;
68
69  DISALLOW_COPY_AND_ASSIGN(CallbackLogger);
70};
71
72}  // namespace
73
74class FileSystemProviderOperationsGetMetadataTest : public testing::Test {
75 protected:
76  FileSystemProviderOperationsGetMetadataTest() {}
77  virtual ~FileSystemProviderOperationsGetMetadataTest() {}
78
79  virtual void SetUp() OVERRIDE {
80    file_system_info_ =
81        ProvidedFileSystemInfo(kExtensionId,
82                               kFileSystemId,
83                               "" /* display_name */,
84                               false /* writable */,
85                               base::FilePath() /* mount_path */);
86  }
87
88  ProvidedFileSystemInfo file_system_info_;
89};
90
91TEST_F(FileSystemProviderOperationsGetMetadataTest, Execute) {
92  using extensions::api::file_system_provider::GetMetadataRequestedOptions;
93
94  util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
95  CallbackLogger callback_logger;
96
97  GetMetadata get_metadata(
98      NULL,
99      file_system_info_,
100      base::FilePath::FromUTF8Unsafe(kDirectoryPath),
101      ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL,
102      base::Bind(&CallbackLogger::OnGetMetadata,
103                 base::Unretained(&callback_logger)));
104  get_metadata.SetDispatchEventImplForTesting(
105      base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
106                 base::Unretained(&dispatcher)));
107
108  EXPECT_TRUE(get_metadata.Execute(kRequestId));
109
110  ASSERT_EQ(1u, dispatcher.events().size());
111  extensions::Event* event = dispatcher.events()[0];
112  EXPECT_EQ(
113      extensions::api::file_system_provider::OnGetMetadataRequested::kEventName,
114      event->event_name);
115  base::ListValue* event_args = event->event_args.get();
116  ASSERT_EQ(1u, event_args->GetSize());
117
118  const base::DictionaryValue* options_as_value = NULL;
119  ASSERT_TRUE(event_args->GetDictionary(0, &options_as_value));
120
121  GetMetadataRequestedOptions options;
122  ASSERT_TRUE(
123      GetMetadataRequestedOptions::Populate(*options_as_value, &options));
124  EXPECT_EQ(kFileSystemId, options.file_system_id);
125  EXPECT_EQ(kRequestId, options.request_id);
126  EXPECT_EQ(kDirectoryPath, options.entry_path);
127  EXPECT_TRUE(options.thumbnail);
128}
129
130TEST_F(FileSystemProviderOperationsGetMetadataTest, Execute_NoListener) {
131  util::LoggingDispatchEventImpl dispatcher(false /* dispatch_reply */);
132  CallbackLogger callback_logger;
133
134  GetMetadata get_metadata(
135      NULL,
136      file_system_info_,
137      base::FilePath::FromUTF8Unsafe(kDirectoryPath),
138      ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL,
139      base::Bind(&CallbackLogger::OnGetMetadata,
140                 base::Unretained(&callback_logger)));
141  get_metadata.SetDispatchEventImplForTesting(
142      base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
143                 base::Unretained(&dispatcher)));
144
145  EXPECT_FALSE(get_metadata.Execute(kRequestId));
146}
147
148TEST_F(FileSystemProviderOperationsGetMetadataTest, OnSuccess) {
149  using extensions::api::file_system_provider_internal::
150      GetMetadataRequestedSuccess::Params;
151
152  util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
153  CallbackLogger callback_logger;
154
155  GetMetadata get_metadata(
156      NULL,
157      file_system_info_,
158      base::FilePath::FromUTF8Unsafe(kDirectoryPath),
159      ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL,
160      base::Bind(&CallbackLogger::OnGetMetadata,
161                 base::Unretained(&callback_logger)));
162  get_metadata.SetDispatchEventImplForTesting(
163      base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
164                 base::Unretained(&dispatcher)));
165
166  EXPECT_TRUE(get_metadata.Execute(kRequestId));
167
168  // Sample input as JSON. Keep in sync with file_system_provider_api.idl.
169  // As for now, it is impossible to create *::Params class directly, not from
170  // base::Value.
171  const std::string input =
172      "[\n"
173      "  \"testing-file-system\",\n"  // kFileSystemId
174      "  2,\n"                        // kRequestId
175      "  {\n"
176      "    \"isDirectory\": false,\n"
177      "    \"name\": \"blueberries.txt\",\n"
178      "    \"size\": 4096,\n"
179      "    \"modificationTime\": {\n"
180      "      \"value\": \"Thu Apr 24 00:46:52 UTC 2014\"\n"
181      "    },\n"
182      "    \"mimeType\": \"text/plain\",\n"              // kMimeType
183      "    \"thumbnail\": \"DaTa:ImAgE/pNg;base64,\"\n"  // kThumbnail
184      "  },\n"
185      "  0\n"  // execution_time
186      "]\n";
187
188  int json_error_code;
189  std::string json_error_msg;
190  scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
191      input, base::JSON_PARSE_RFC, &json_error_code, &json_error_msg));
192  ASSERT_TRUE(value.get()) << json_error_msg;
193
194  base::ListValue* value_as_list;
195  ASSERT_TRUE(value->GetAsList(&value_as_list));
196  scoped_ptr<Params> params(Params::Create(*value_as_list));
197  ASSERT_TRUE(params.get());
198  scoped_ptr<RequestValue> request_value(
199      RequestValue::CreateForGetMetadataSuccess(params.Pass()));
200  ASSERT_TRUE(request_value.get());
201
202  const bool has_more = false;
203  get_metadata.OnSuccess(kRequestId, request_value.Pass(), has_more);
204
205  ASSERT_EQ(1u, callback_logger.events().size());
206  CallbackLogger::Event* event = callback_logger.events()[0];
207  EXPECT_EQ(base::File::FILE_OK, event->result());
208
209  const EntryMetadata* metadata = event->metadata();
210  EXPECT_FALSE(metadata->is_directory);
211  EXPECT_EQ(4096, metadata->size);
212  base::Time expected_time;
213  EXPECT_TRUE(
214      base::Time::FromString("Thu Apr 24 00:46:52 UTC 2014", &expected_time));
215  EXPECT_EQ(expected_time, metadata->modification_time);
216  EXPECT_EQ(kMimeType, metadata->mime_type);
217  EXPECT_EQ(kThumbnail, metadata->thumbnail);
218}
219
220TEST_F(FileSystemProviderOperationsGetMetadataTest,
221       OnSuccess_InvalidThumbnail) {
222  using extensions::api::file_system_provider_internal::
223      GetMetadataRequestedSuccess::Params;
224
225  util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
226  CallbackLogger callback_logger;
227
228  GetMetadata get_metadata(
229      NULL,
230      file_system_info_,
231      base::FilePath::FromUTF8Unsafe(kDirectoryPath),
232      ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL,
233      base::Bind(&CallbackLogger::OnGetMetadata,
234                 base::Unretained(&callback_logger)));
235  get_metadata.SetDispatchEventImplForTesting(
236      base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
237                 base::Unretained(&dispatcher)));
238
239  EXPECT_TRUE(get_metadata.Execute(kRequestId));
240
241  // Sample input as JSON. Keep in sync with file_system_provider_api.idl.
242  // As for now, it is impossible to create *::Params class directly, not from
243  // base::Value.
244  const std::string input =
245      "[\n"
246      "  \"testing-file-system\",\n"  // kFileSystemId
247      "  2,\n"                        // kRequestId
248      "  {\n"
249      "    \"isDirectory\": false,\n"
250      "    \"name\": \"blueberries.txt\",\n"
251      "    \"size\": 4096,\n"
252      "    \"modificationTime\": {\n"
253      "      \"value\": \"Thu Apr 24 00:46:52 UTC 2014\"\n"
254      "    },\n"
255      "    \"mimeType\": \"text/plain\",\n"                  // kMimeType
256      "    \"thumbnail\": \"http://www.foobar.com/evil\"\n"  // kThumbnail
257      "  },\n"
258      "  0\n"  // execution_time
259      "]\n";
260
261  int json_error_code;
262  std::string json_error_msg;
263  scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
264      input, base::JSON_PARSE_RFC, &json_error_code, &json_error_msg));
265  ASSERT_TRUE(value.get()) << json_error_msg;
266
267  base::ListValue* value_as_list;
268  ASSERT_TRUE(value->GetAsList(&value_as_list));
269  scoped_ptr<Params> params(Params::Create(*value_as_list));
270  ASSERT_TRUE(params.get());
271  scoped_ptr<RequestValue> request_value(
272      RequestValue::CreateForGetMetadataSuccess(params.Pass()));
273  ASSERT_TRUE(request_value.get());
274
275  const bool has_more = false;
276  get_metadata.OnSuccess(kRequestId, request_value.Pass(), has_more);
277
278  ASSERT_EQ(1u, callback_logger.events().size());
279  CallbackLogger::Event* event = callback_logger.events()[0];
280  EXPECT_EQ(base::File::FILE_ERROR_IO, event->result());
281
282  const EntryMetadata* metadata = event->metadata();
283  EXPECT_FALSE(metadata);
284}
285
286TEST_F(FileSystemProviderOperationsGetMetadataTest, OnError) {
287  util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
288  CallbackLogger callback_logger;
289
290  GetMetadata get_metadata(
291      NULL,
292      file_system_info_,
293      base::FilePath::FromUTF8Unsafe(kDirectoryPath),
294      ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL,
295      base::Bind(&CallbackLogger::OnGetMetadata,
296                 base::Unretained(&callback_logger)));
297  get_metadata.SetDispatchEventImplForTesting(
298      base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
299                 base::Unretained(&dispatcher)));
300
301  EXPECT_TRUE(get_metadata.Execute(kRequestId));
302
303  get_metadata.OnError(kRequestId,
304                       scoped_ptr<RequestValue>(new RequestValue()),
305                       base::File::FILE_ERROR_TOO_MANY_OPENED);
306
307  ASSERT_EQ(1u, callback_logger.events().size());
308  CallbackLogger::Event* event = callback_logger.events()[0];
309  EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, event->result());
310}
311
312}  // namespace operations
313}  // namespace file_system_provider
314}  // namespace chromeos
315