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/read_directory.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/files/file.h"
14 #include "base/files/file_path.h"
15 #include "base/json/json_reader.h"
16 #include "base/macros.h"
17 #include "base/values.h"
18 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
19 #include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
20 #include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
21 #include "chrome/common/extensions/api/file_system_provider.h"
22 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
23 #include "chrome/common/extensions/api/file_system_provider_internal.h"
24 #include "components/services/filesystem/public/mojom/types.mojom.h"
25 #include "extensions/browser/event_router.h"
26 #include "storage/browser/file_system/async_file_util.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 namespace chromeos {
30 namespace file_system_provider {
31 namespace operations {
32 namespace {
33
34 const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
35 const char kFileSystemId[] = "testing-file-system";
36 const int kRequestId = 2;
37 const base::FilePath::CharType kDirectoryPath[] =
38 FILE_PATH_LITERAL("/directory");
39
40 // Callback invocation logger. Acts as a fileapi end-point.
41 class CallbackLogger {
42 public:
43 class Event {
44 public:
Event(base::File::Error result,storage::AsyncFileUtil::EntryList entry_list,bool has_more)45 Event(base::File::Error result,
46 storage::AsyncFileUtil::EntryList entry_list,
47 bool has_more)
48 : result_(result),
49 entry_list_(std::move(entry_list)),
50 has_more_(has_more) {}
~Event()51 virtual ~Event() {}
52
result()53 base::File::Error result() { return result_; }
entry_list()54 const storage::AsyncFileUtil::EntryList& entry_list() {
55 return entry_list_;
56 }
has_more()57 bool has_more() { return has_more_; }
58
59 private:
60 base::File::Error result_;
61 storage::AsyncFileUtil::EntryList entry_list_;
62 bool has_more_;
63
64 DISALLOW_COPY_AND_ASSIGN(Event);
65 };
66
CallbackLogger()67 CallbackLogger() {}
~CallbackLogger()68 virtual ~CallbackLogger() {}
69
OnReadDirectory(base::File::Error result,storage::AsyncFileUtil::EntryList entry_list,bool has_more)70 void OnReadDirectory(base::File::Error result,
71 storage::AsyncFileUtil::EntryList entry_list,
72 bool has_more) {
73 events_.push_back(
74 std::make_unique<Event>(result, std::move(entry_list), has_more));
75 }
76
events()77 std::vector<std::unique_ptr<Event>>& events() { return events_; }
78
79 private:
80 std::vector<std::unique_ptr<Event>> events_;
81
82 DISALLOW_COPY_AND_ASSIGN(CallbackLogger);
83 };
84
85 // Returns the request value as |result| in case of successful parse.
CreateRequestValueFromJSON(const std::string & json,std::unique_ptr<RequestValue> * result)86 void CreateRequestValueFromJSON(const std::string& json,
87 std::unique_ptr<RequestValue>* result) {
88 using extensions::api::file_system_provider_internal::
89 ReadDirectoryRequestedSuccess::Params;
90
91 base::JSONReader::ValueWithError parsed_json =
92 base::JSONReader::ReadAndReturnValueWithError(json);
93 ASSERT_TRUE(parsed_json.value) << parsed_json.error_message;
94
95 base::ListValue* value_as_list;
96 ASSERT_TRUE(parsed_json.value->GetAsList(&value_as_list));
97 std::unique_ptr<Params> params(Params::Create(*value_as_list));
98 ASSERT_TRUE(params.get());
99 *result = RequestValue::CreateForReadDirectorySuccess(std::move(params));
100 ASSERT_TRUE(result->get());
101 }
102
103 } // namespace
104
105 class FileSystemProviderOperationsReadDirectoryTest : public testing::Test {
106 protected:
FileSystemProviderOperationsReadDirectoryTest()107 FileSystemProviderOperationsReadDirectoryTest() {}
~FileSystemProviderOperationsReadDirectoryTest()108 ~FileSystemProviderOperationsReadDirectoryTest() override {}
109
SetUp()110 void SetUp() override {
111 file_system_info_ = ProvidedFileSystemInfo(
112 kExtensionId, MountOptions(kFileSystemId, "" /* display_name */),
113 base::FilePath(), false /* configurable */, true /* watchable */,
114 extensions::SOURCE_FILE, IconSet());
115 }
116
117 ProvidedFileSystemInfo file_system_info_;
118 };
119
TEST_F(FileSystemProviderOperationsReadDirectoryTest,Execute)120 TEST_F(FileSystemProviderOperationsReadDirectoryTest, Execute) {
121 using extensions::api::file_system_provider::ReadDirectoryRequestedOptions;
122
123 util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
124 CallbackLogger callback_logger;
125
126 ReadDirectory read_directory(NULL, file_system_info_,
127 base::FilePath(kDirectoryPath),
128 base::Bind(&CallbackLogger::OnReadDirectory,
129 base::Unretained(&callback_logger)));
130 read_directory.SetDispatchEventImplForTesting(
131 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
132 base::Unretained(&dispatcher)));
133
134 EXPECT_TRUE(read_directory.Execute(kRequestId));
135
136 ASSERT_EQ(1u, dispatcher.events().size());
137 extensions::Event* event = dispatcher.events()[0].get();
138 EXPECT_EQ(extensions::api::file_system_provider::OnReadDirectoryRequested::
139 kEventName,
140 event->event_name);
141 base::ListValue* event_args = event->event_args.get();
142 ASSERT_EQ(1u, event_args->GetSize());
143
144 const base::DictionaryValue* options_as_value = NULL;
145 ASSERT_TRUE(event_args->GetDictionary(0, &options_as_value));
146
147 ReadDirectoryRequestedOptions options;
148 ASSERT_TRUE(
149 ReadDirectoryRequestedOptions::Populate(*options_as_value, &options));
150 EXPECT_EQ(kFileSystemId, options.file_system_id);
151 EXPECT_EQ(kRequestId, options.request_id);
152 EXPECT_EQ(kDirectoryPath, options.directory_path);
153 }
154
TEST_F(FileSystemProviderOperationsReadDirectoryTest,Execute_NoListener)155 TEST_F(FileSystemProviderOperationsReadDirectoryTest, Execute_NoListener) {
156 util::LoggingDispatchEventImpl dispatcher(false /* dispatch_reply */);
157 CallbackLogger callback_logger;
158
159 ReadDirectory read_directory(NULL, file_system_info_,
160 base::FilePath(kDirectoryPath),
161 base::Bind(&CallbackLogger::OnReadDirectory,
162 base::Unretained(&callback_logger)));
163 read_directory.SetDispatchEventImplForTesting(
164 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
165 base::Unretained(&dispatcher)));
166
167 EXPECT_FALSE(read_directory.Execute(kRequestId));
168 }
169
TEST_F(FileSystemProviderOperationsReadDirectoryTest,OnSuccess)170 TEST_F(FileSystemProviderOperationsReadDirectoryTest, OnSuccess) {
171 util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
172 CallbackLogger callback_logger;
173
174 ReadDirectory read_directory(NULL, file_system_info_,
175 base::FilePath(kDirectoryPath),
176 base::Bind(&CallbackLogger::OnReadDirectory,
177 base::Unretained(&callback_logger)));
178 read_directory.SetDispatchEventImplForTesting(
179 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
180 base::Unretained(&dispatcher)));
181
182 EXPECT_TRUE(read_directory.Execute(kRequestId));
183
184 // Sample input as JSON. Keep in sync with file_system_provider_api.idl.
185 // As for now, it is impossible to create *::Params class directly, not from
186 // base::Value.
187 const std::string input =
188 "[\n"
189 " \"testing-file-system\",\n" // kFileSystemId
190 " 2,\n" // kRequestId
191 " [\n"
192 " {\n"
193 " \"isDirectory\": false,\n"
194 " \"name\": \"blueberries.txt\"\n"
195 " }\n"
196 " ],\n"
197 " false,\n" // has_more
198 " 0\n" // execution_time
199 "]\n";
200 std::unique_ptr<RequestValue> request_value;
201 ASSERT_NO_FATAL_FAILURE(CreateRequestValueFromJSON(input, &request_value));
202
203 const bool has_more = false;
204 read_directory.OnSuccess(kRequestId, std::move(request_value), has_more);
205
206 ASSERT_EQ(1u, callback_logger.events().size());
207 CallbackLogger::Event* event = callback_logger.events()[0].get();
208 EXPECT_EQ(base::File::FILE_OK, event->result());
209
210 ASSERT_EQ(1u, event->entry_list().size());
211 const filesystem::mojom::DirectoryEntry entry = event->entry_list()[0];
212 EXPECT_EQ(entry.type, filesystem::mojom::FsFileType::REGULAR_FILE);
213 EXPECT_EQ("blueberries.txt", entry.name.value());
214 }
215
TEST_F(FileSystemProviderOperationsReadDirectoryTest,OnSuccess_InvalidMetadata)216 TEST_F(FileSystemProviderOperationsReadDirectoryTest,
217 OnSuccess_InvalidMetadata) {
218 util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
219 CallbackLogger callback_logger;
220
221 ReadDirectory read_directory(NULL, file_system_info_,
222 base::FilePath(kDirectoryPath),
223 base::Bind(&CallbackLogger::OnReadDirectory,
224 base::Unretained(&callback_logger)));
225 read_directory.SetDispatchEventImplForTesting(
226 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
227 base::Unretained(&dispatcher)));
228
229 EXPECT_TRUE(read_directory.Execute(kRequestId));
230
231 // Sample input as JSON. Keep in sync with file_system_provider_api.idl.
232 // As for now, it is impossible to create *::Params class directly, not from
233 // base::Value.
234 const std::string input =
235 "[\n"
236 " \"testing-file-system\",\n" // kFileSystemId
237 " 2,\n" // kRequestId
238 " [\n"
239 " {\n"
240 " \"isDirectory\": false,\n"
241 " \"name\": \"blue/berries.txt\"\n"
242 " }\n"
243 " ],\n"
244 " false,\n" // has_more
245 " 0\n" // execution_time
246 "]\n";
247 std::unique_ptr<RequestValue> request_value;
248 ASSERT_NO_FATAL_FAILURE(CreateRequestValueFromJSON(input, &request_value));
249
250 const bool has_more = false;
251 read_directory.OnSuccess(kRequestId, std::move(request_value), has_more);
252
253 ASSERT_EQ(1u, callback_logger.events().size());
254 CallbackLogger::Event* event = callback_logger.events()[0].get();
255 EXPECT_EQ(base::File::FILE_ERROR_IO, event->result());
256
257 EXPECT_EQ(0u, event->entry_list().size());
258 }
259
TEST_F(FileSystemProviderOperationsReadDirectoryTest,OnError)260 TEST_F(FileSystemProviderOperationsReadDirectoryTest, OnError) {
261 util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
262 CallbackLogger callback_logger;
263
264 ReadDirectory read_directory(NULL, file_system_info_,
265 base::FilePath(kDirectoryPath),
266 base::Bind(&CallbackLogger::OnReadDirectory,
267 base::Unretained(&callback_logger)));
268 read_directory.SetDispatchEventImplForTesting(
269 base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
270 base::Unretained(&dispatcher)));
271
272 EXPECT_TRUE(read_directory.Execute(kRequestId));
273
274 read_directory.OnError(kRequestId,
275 std::unique_ptr<RequestValue>(new RequestValue()),
276 base::File::FILE_ERROR_TOO_MANY_OPENED);
277
278 ASSERT_EQ(1u, callback_logger.events().size());
279 CallbackLogger::Event* event = callback_logger.events()[0].get();
280 EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, event->result());
281 ASSERT_EQ(0u, event->entry_list().size());
282 }
283
284 } // namespace operations
285 } // namespace file_system_provider
286 } // namespace chromeos
287