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