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.
5 #include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
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_capabilities/file_system_provider_capabilities_handler.h"
18 #include "chrome/common/extensions/api/file_system_provider_internal.h"
19 #include "extensions/browser/event_router.h"
20 #include "storage/browser/fileapi/async_file_util.h"
21 #include "testing/gtest/include/gtest/gtest.h"
24 namespace file_system_provider
{
25 namespace operations
{
28 const char kExtensionId
[] = "mbflcebpggnecokmikipoihdbecnjfoj";
29 const char kFileSystemId
[] = "testing-file-system";
30 const char kMimeType
[] = "text/plain";
31 const int kRequestId
= 2;
32 const base::FilePath::CharType kDirectoryPath
[] =
33 FILE_PATH_LITERAL("/directory");
35 // URLs are case insensitive, so it should pass the sanity check.
36 const char kThumbnail
[] = "DaTa:ImAgE/pNg;base64,";
38 // Returns the request value as |result| in case of successful parse.
39 void CreateRequestValueFromJSON(const std::string
& json
,
40 scoped_ptr
<RequestValue
>* result
) {
41 using extensions::api::file_system_provider_internal::
42 GetMetadataRequestedSuccess::Params
;
45 std::string json_error_msg
;
46 scoped_ptr
<base::Value
> value
= base::JSONReader::ReadAndReturnError(
47 json
, base::JSON_PARSE_RFC
, &json_error_code
, &json_error_msg
);
48 ASSERT_TRUE(value
.get()) << json_error_msg
;
50 base::ListValue
* value_as_list
;
51 ASSERT_TRUE(value
->GetAsList(&value_as_list
));
52 scoped_ptr
<Params
> params(Params::Create(*value_as_list
));
53 ASSERT_TRUE(params
.get());
54 *result
= RequestValue::CreateForGetMetadataSuccess(params
.Pass());
55 ASSERT_TRUE(result
->get());
58 // Callback invocation logger. Acts as a fileapi end-point.
59 class CallbackLogger
{
63 Event(scoped_ptr
<EntryMetadata
> metadata
, base::File::Error result
)
64 : metadata_(metadata
.Pass()), result_(result
) {}
67 const EntryMetadata
* metadata() const { return metadata_
.get(); }
68 base::File::Error
result() const { return result_
; }
71 scoped_ptr
<EntryMetadata
> metadata_
;
72 base::File::Error result_
;
74 DISALLOW_COPY_AND_ASSIGN(Event
);
78 virtual ~CallbackLogger() {}
80 void OnGetMetadata(scoped_ptr
<EntryMetadata
> metadata
,
81 base::File::Error result
) {
82 events_
.push_back(new Event(metadata
.Pass(), result
));
85 const ScopedVector
<Event
>& events() const { return events_
; }
88 ScopedVector
<Event
> events_
;
90 DISALLOW_COPY_AND_ASSIGN(CallbackLogger
);
95 class FileSystemProviderOperationsGetMetadataTest
: public testing::Test
{
97 FileSystemProviderOperationsGetMetadataTest() {}
98 ~FileSystemProviderOperationsGetMetadataTest() override
{}
100 void SetUp() override
{
101 file_system_info_
= ProvidedFileSystemInfo(
102 kExtensionId
, MountOptions(kFileSystemId
, "" /* display_name */),
103 base::FilePath(), false /* configurable */, true /* watchable */,
104 extensions::SOURCE_FILE
);
107 ProvidedFileSystemInfo file_system_info_
;
110 TEST_F(FileSystemProviderOperationsGetMetadataTest
, ValidateName
) {
111 EXPECT_TRUE(ValidateName("hello-world!@#$%^&*()-_=+\"':,.<>?[]{}|\\",
112 false /* root_entry */));
113 EXPECT_FALSE(ValidateName("hello-world!@#$%^&*()-_=+\"':,.<>?[]{}|\\",
114 true /* root_entry */));
115 EXPECT_FALSE(ValidateName("", false /* root_path */));
116 EXPECT_TRUE(ValidateName("", true /* root_path */));
117 EXPECT_FALSE(ValidateName("hello/world", false /* root_path */));
118 EXPECT_FALSE(ValidateName("hello/world", true /* root_path */));
121 TEST_F(FileSystemProviderOperationsGetMetadataTest
, ValidateIDLEntryMetadata
) {
122 using extensions::api::file_system_provider::EntryMetadata
;
123 const std::string kValidFileName
= "hello-world";
124 const std::string kValidThumbnailUrl
= "data:something";
126 // Correct metadata for non-root.
128 EntryMetadata metadata
;
129 metadata
.name
= kValidFileName
;
130 metadata
.modification_time
.additional_properties
.SetString(
131 "value", "invalid-date-time"); // Invalid modification time is OK.
132 metadata
.thumbnail
.reset(new std::string(kValidThumbnailUrl
));
133 EXPECT_TRUE(ValidateIDLEntryMetadata(metadata
, false /* root_path */));
136 // Correct metadata for non-root (without thumbnail).
138 EntryMetadata metadata
;
139 metadata
.name
= kValidFileName
;
140 metadata
.modification_time
.additional_properties
.SetString(
141 "value", "invalid-date-time"); // Invalid modification time is OK.
142 EXPECT_TRUE(ValidateIDLEntryMetadata(metadata
, false /* root_path */));
145 // Correct metadata for root.
147 EntryMetadata metadata
;
149 metadata
.modification_time
.additional_properties
.SetString(
150 "value", "invalid-date-time"); // Invalid modification time is OK.
151 EXPECT_TRUE(ValidateIDLEntryMetadata(metadata
, true /* root_path */));
154 // Invalid characters in the name.
156 EntryMetadata metadata
;
157 metadata
.name
= "hello/world";
158 metadata
.modification_time
.additional_properties
.SetString(
159 "value", "invalid-date-time"); // Invalid modification time is OK.
160 metadata
.thumbnail
.reset(new std::string(kValidThumbnailUrl
));
161 EXPECT_FALSE(ValidateIDLEntryMetadata(metadata
, false /* root_path */));
164 // Empty name for non-root.
166 EntryMetadata metadata
;
168 metadata
.modification_time
.additional_properties
.SetString(
169 "value", "invalid-date-time"); // Invalid modification time is OK.
170 metadata
.thumbnail
.reset(new std::string(kValidThumbnailUrl
));
171 EXPECT_FALSE(ValidateIDLEntryMetadata(metadata
, false /* root_path */));
174 // Missing date time.
176 EntryMetadata metadata
;
177 metadata
.name
= kValidFileName
;
178 metadata
.thumbnail
.reset(new std::string(kValidThumbnailUrl
));
179 EXPECT_FALSE(ValidateIDLEntryMetadata(metadata
, false /* root_path */));
182 // Invalid thumbnail.
184 EntryMetadata metadata
;
185 metadata
.name
= kValidFileName
;
186 metadata
.modification_time
.additional_properties
.SetString(
187 "value", "invalid-date-time"); // Invalid modification time is OK.
188 metadata
.thumbnail
.reset(new std::string("http://invalid-scheme"));
189 EXPECT_FALSE(ValidateIDLEntryMetadata(metadata
, false /* root_path */));
193 TEST_F(FileSystemProviderOperationsGetMetadataTest
, Execute
) {
194 using extensions::api::file_system_provider::GetMetadataRequestedOptions
;
196 util::LoggingDispatchEventImpl
dispatcher(true /* dispatch_reply */);
197 CallbackLogger callback_logger
;
199 GetMetadata
get_metadata(
200 NULL
, file_system_info_
, base::FilePath(kDirectoryPath
),
201 ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL
,
202 base::Bind(&CallbackLogger::OnGetMetadata
,
203 base::Unretained(&callback_logger
)));
204 get_metadata
.SetDispatchEventImplForTesting(
205 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl
,
206 base::Unretained(&dispatcher
)));
208 EXPECT_TRUE(get_metadata
.Execute(kRequestId
));
210 ASSERT_EQ(1u, dispatcher
.events().size());
211 extensions::Event
* event
= dispatcher
.events()[0];
213 extensions::api::file_system_provider::OnGetMetadataRequested::kEventName
,
215 base::ListValue
* event_args
= event
->event_args
.get();
216 ASSERT_EQ(1u, event_args
->GetSize());
218 const base::DictionaryValue
* options_as_value
= NULL
;
219 ASSERT_TRUE(event_args
->GetDictionary(0, &options_as_value
));
221 GetMetadataRequestedOptions options
;
223 GetMetadataRequestedOptions::Populate(*options_as_value
, &options
));
224 EXPECT_EQ(kFileSystemId
, options
.file_system_id
);
225 EXPECT_EQ(kRequestId
, options
.request_id
);
226 EXPECT_EQ(kDirectoryPath
, options
.entry_path
);
227 EXPECT_TRUE(options
.thumbnail
);
230 TEST_F(FileSystemProviderOperationsGetMetadataTest
, Execute_NoListener
) {
231 util::LoggingDispatchEventImpl
dispatcher(false /* dispatch_reply */);
232 CallbackLogger callback_logger
;
234 GetMetadata
get_metadata(
235 NULL
, file_system_info_
, base::FilePath(kDirectoryPath
),
236 ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL
,
237 base::Bind(&CallbackLogger::OnGetMetadata
,
238 base::Unretained(&callback_logger
)));
239 get_metadata
.SetDispatchEventImplForTesting(
240 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl
,
241 base::Unretained(&dispatcher
)));
243 EXPECT_FALSE(get_metadata
.Execute(kRequestId
));
246 TEST_F(FileSystemProviderOperationsGetMetadataTest
, OnSuccess
) {
247 util::LoggingDispatchEventImpl
dispatcher(true /* dispatch_reply */);
248 CallbackLogger callback_logger
;
250 GetMetadata
get_metadata(
251 NULL
, file_system_info_
, base::FilePath(kDirectoryPath
),
252 ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL
,
253 base::Bind(&CallbackLogger::OnGetMetadata
,
254 base::Unretained(&callback_logger
)));
255 get_metadata
.SetDispatchEventImplForTesting(
256 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl
,
257 base::Unretained(&dispatcher
)));
259 EXPECT_TRUE(get_metadata
.Execute(kRequestId
));
261 // Sample input as JSON. Keep in sync with file_system_provider_api.idl.
262 // As for now, it is impossible to create *::Params class directly, not from
264 const std::string input
=
266 " \"testing-file-system\",\n" // kFileSystemId
267 " 2,\n" // kRequestId
269 " \"isDirectory\": false,\n"
270 " \"name\": \"blueberries.txt\",\n"
272 " \"modificationTime\": {\n"
273 " \"value\": \"Thu Apr 24 00:46:52 UTC 2014\"\n"
275 " \"mimeType\": \"text/plain\",\n" // kMimeType
276 " \"thumbnail\": \"DaTa:ImAgE/pNg;base64,\"\n" // kThumbnail
278 " 0\n" // execution_time
280 scoped_ptr
<RequestValue
> request_value
;
281 ASSERT_NO_FATAL_FAILURE(CreateRequestValueFromJSON(input
, &request_value
));
283 const bool has_more
= false;
284 get_metadata
.OnSuccess(kRequestId
, request_value
.Pass(), has_more
);
286 ASSERT_EQ(1u, callback_logger
.events().size());
287 CallbackLogger::Event
* event
= callback_logger
.events()[0];
288 EXPECT_EQ(base::File::FILE_OK
, event
->result());
290 const EntryMetadata
* metadata
= event
->metadata();
291 EXPECT_FALSE(metadata
->is_directory
);
292 EXPECT_EQ(4096, metadata
->size
);
293 base::Time expected_time
;
295 base::Time::FromString("Thu Apr 24 00:46:52 UTC 2014", &expected_time
));
296 EXPECT_EQ(expected_time
, metadata
->modification_time
);
297 EXPECT_EQ(kMimeType
, metadata
->mime_type
);
298 EXPECT_EQ(kThumbnail
, metadata
->thumbnail
);
301 TEST_F(FileSystemProviderOperationsGetMetadataTest
, OnSuccess_InvalidMetadata
) {
302 util::LoggingDispatchEventImpl
dispatcher(true /* dispatch_reply */);
303 CallbackLogger callback_logger
;
305 GetMetadata
get_metadata(
306 NULL
, file_system_info_
, base::FilePath(kDirectoryPath
),
307 ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL
,
308 base::Bind(&CallbackLogger::OnGetMetadata
,
309 base::Unretained(&callback_logger
)));
310 get_metadata
.SetDispatchEventImplForTesting(
311 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl
,
312 base::Unretained(&dispatcher
)));
314 EXPECT_TRUE(get_metadata
.Execute(kRequestId
));
316 // Sample input as JSON. Keep in sync with file_system_provider_api.idl.
317 // As for now, it is impossible to create *::Params class directly, not from
319 const std::string input
=
321 " \"testing-file-system\",\n" // kFileSystemId
322 " 2,\n" // kRequestId
324 " \"isDirectory\": false,\n"
325 " \"name\": \"blue/berries.txt\",\n"
327 " \"modificationTime\": {\n"
328 " \"value\": \"Thu Apr 24 00:46:52 UTC 2014\"\n"
330 " \"mimeType\": \"text/plain\",\n" // kMimeType
331 " \"thumbnail\": \"http://www.foobar.com/evil\"\n" // kThumbnail
333 " 0\n" // execution_time
336 scoped_ptr
<RequestValue
> request_value
;
337 ASSERT_NO_FATAL_FAILURE(CreateRequestValueFromJSON(input
, &request_value
));
339 const bool has_more
= false;
340 get_metadata
.OnSuccess(kRequestId
, request_value
.Pass(), has_more
);
342 ASSERT_EQ(1u, callback_logger
.events().size());
343 CallbackLogger::Event
* event
= callback_logger
.events()[0];
344 EXPECT_EQ(base::File::FILE_ERROR_IO
, event
->result());
346 const EntryMetadata
* metadata
= event
->metadata();
347 EXPECT_FALSE(metadata
);
350 TEST_F(FileSystemProviderOperationsGetMetadataTest
, OnError
) {
351 util::LoggingDispatchEventImpl
dispatcher(true /* dispatch_reply */);
352 CallbackLogger callback_logger
;
354 GetMetadata
get_metadata(
355 NULL
, file_system_info_
, base::FilePath(kDirectoryPath
),
356 ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL
,
357 base::Bind(&CallbackLogger::OnGetMetadata
,
358 base::Unretained(&callback_logger
)));
359 get_metadata
.SetDispatchEventImplForTesting(
360 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl
,
361 base::Unretained(&dispatcher
)));
363 EXPECT_TRUE(get_metadata
.Execute(kRequestId
));
365 get_metadata
.OnError(kRequestId
,
366 scoped_ptr
<RequestValue
>(new RequestValue()),
367 base::File::FILE_ERROR_TOO_MANY_OPENED
);
369 ASSERT_EQ(1u, callback_logger
.events().size());
370 CallbackLogger::Event
* event
= callback_logger
.events()[0];
371 EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED
, event
->result());
374 } // namespace operations
375 } // namespace file_system_provider
376 } // namespace chromeos