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/open_file.h"
6
7#include <string>
8#include <vector>
9
10#include "base/files/file.h"
11#include "base/files/file_path.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/memory/scoped_vector.h"
14#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
15#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.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 int kRequestId = 2;
30const base::FilePath::CharType kFilePath[] = "/directory/blueberries.txt";
31
32// Callback invocation logger. Acts as a fileapi end-point.
33class CallbackLogger {
34 public:
35  class Event {
36   public:
37    Event(int file_handle, base::File::Error result)
38        : file_handle_(file_handle), result_(result) {}
39    virtual ~Event() {}
40
41    int file_handle() { return file_handle_; }
42    base::File::Error result() { return result_; }
43
44   private:
45    int file_handle_;
46    base::File::Error result_;
47
48    DISALLOW_COPY_AND_ASSIGN(Event);
49  };
50
51  CallbackLogger() {}
52  virtual ~CallbackLogger() {}
53
54  void OnOpenFile(int file_handle, base::File::Error result) {
55    events_.push_back(new Event(file_handle, result));
56  }
57
58  ScopedVector<Event>& events() { return events_; }
59
60 private:
61  ScopedVector<Event> events_;
62  bool dispatch_reply_;
63
64  DISALLOW_COPY_AND_ASSIGN(CallbackLogger);
65};
66
67}  // namespace
68
69class FileSystemProviderOperationsOpenFileTest : public testing::Test {
70 protected:
71  FileSystemProviderOperationsOpenFileTest() {}
72  virtual ~FileSystemProviderOperationsOpenFileTest() {}
73
74  virtual void SetUp() OVERRIDE {
75    file_system_info_ =
76        ProvidedFileSystemInfo(kExtensionId,
77                               kFileSystemId,
78                               "" /* display_name */,
79                               false /* writable */,
80                               base::FilePath() /* mount_path */);
81  }
82
83  ProvidedFileSystemInfo file_system_info_;
84};
85
86TEST_F(FileSystemProviderOperationsOpenFileTest, Execute) {
87  using extensions::api::file_system_provider::OpenFileRequestedOptions;
88
89  util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
90  CallbackLogger callback_logger;
91
92  OpenFile open_file(NULL,
93                     file_system_info_,
94                     base::FilePath::FromUTF8Unsafe(kFilePath),
95                     ProvidedFileSystemInterface::OPEN_FILE_MODE_READ,
96                     base::Bind(&CallbackLogger::OnOpenFile,
97                                base::Unretained(&callback_logger)));
98  open_file.SetDispatchEventImplForTesting(
99      base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
100                 base::Unretained(&dispatcher)));
101
102  EXPECT_TRUE(open_file.Execute(kRequestId));
103
104  ASSERT_EQ(1u, dispatcher.events().size());
105  extensions::Event* event = dispatcher.events()[0];
106  EXPECT_EQ(
107      extensions::api::file_system_provider::OnOpenFileRequested::kEventName,
108      event->event_name);
109  base::ListValue* event_args = event->event_args.get();
110  ASSERT_EQ(1u, event_args->GetSize());
111
112  const base::DictionaryValue* options_as_value = NULL;
113  ASSERT_TRUE(event_args->GetDictionary(0, &options_as_value));
114
115  OpenFileRequestedOptions options;
116  ASSERT_TRUE(OpenFileRequestedOptions::Populate(*options_as_value, &options));
117  EXPECT_EQ(kFileSystemId, options.file_system_id);
118  EXPECT_EQ(kRequestId, options.request_id);
119  EXPECT_EQ(kFilePath, options.file_path);
120  EXPECT_EQ(extensions::api::file_system_provider::OPEN_FILE_MODE_READ,
121            options.mode);
122}
123
124TEST_F(FileSystemProviderOperationsOpenFileTest, Execute_NoListener) {
125  util::LoggingDispatchEventImpl dispatcher(false /* dispatch_reply */);
126  CallbackLogger callback_logger;
127
128  OpenFile open_file(NULL,
129                     file_system_info_,
130                     base::FilePath::FromUTF8Unsafe(kFilePath),
131                     ProvidedFileSystemInterface::OPEN_FILE_MODE_READ,
132                     base::Bind(&CallbackLogger::OnOpenFile,
133                                base::Unretained(&callback_logger)));
134  open_file.SetDispatchEventImplForTesting(
135      base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
136                 base::Unretained(&dispatcher)));
137
138  EXPECT_FALSE(open_file.Execute(kRequestId));
139}
140
141TEST_F(FileSystemProviderOperationsOpenFileTest, Execute_ReadOnly) {
142  util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
143  CallbackLogger callback_logger;
144
145  const ProvidedFileSystemInfo read_only_file_system_info(
146      kExtensionId,
147      kFileSystemId,
148      "" /* file_system_name */,
149      false /* writable */,
150      base::FilePath() /* mount_path */);
151
152  // Opening for read on a read-only file system is allowed.
153  {
154    OpenFile open_file(NULL,
155                       read_only_file_system_info,
156                       base::FilePath::FromUTF8Unsafe(kFilePath),
157                       ProvidedFileSystemInterface::OPEN_FILE_MODE_READ,
158                       base::Bind(&CallbackLogger::OnOpenFile,
159                                  base::Unretained(&callback_logger)));
160    open_file.SetDispatchEventImplForTesting(
161        base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
162                   base::Unretained(&dispatcher)));
163
164    EXPECT_TRUE(open_file.Execute(kRequestId));
165  }
166
167  // Opening for write on a read-only file system is forbidden and must fail.
168  {
169    OpenFile open_file(NULL,
170                       read_only_file_system_info,
171                       base::FilePath::FromUTF8Unsafe(kFilePath),
172                       ProvidedFileSystemInterface::OPEN_FILE_MODE_WRITE,
173                       base::Bind(&CallbackLogger::OnOpenFile,
174                                  base::Unretained(&callback_logger)));
175    open_file.SetDispatchEventImplForTesting(
176        base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
177                   base::Unretained(&dispatcher)));
178
179    EXPECT_FALSE(open_file.Execute(kRequestId));
180  }
181}
182
183TEST_F(FileSystemProviderOperationsOpenFileTest, OnSuccess) {
184  util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
185  CallbackLogger callback_logger;
186
187  OpenFile open_file(NULL,
188                     file_system_info_,
189                     base::FilePath::FromUTF8Unsafe(kFilePath),
190                     ProvidedFileSystemInterface::OPEN_FILE_MODE_READ,
191                     base::Bind(&CallbackLogger::OnOpenFile,
192                                base::Unretained(&callback_logger)));
193  open_file.SetDispatchEventImplForTesting(
194      base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
195                 base::Unretained(&dispatcher)));
196
197  EXPECT_TRUE(open_file.Execute(kRequestId));
198
199  open_file.OnSuccess(kRequestId,
200                      scoped_ptr<RequestValue>(new RequestValue()),
201                      false /* has_more */);
202  ASSERT_EQ(1u, callback_logger.events().size());
203  CallbackLogger::Event* event = callback_logger.events()[0];
204  EXPECT_EQ(base::File::FILE_OK, event->result());
205  EXPECT_LT(0, event->file_handle());
206}
207
208TEST_F(FileSystemProviderOperationsOpenFileTest, OnError) {
209  util::LoggingDispatchEventImpl dispatcher(true /* dispatch_reply */);
210  CallbackLogger callback_logger;
211
212  OpenFile open_file(NULL,
213                     file_system_info_,
214                     base::FilePath::FromUTF8Unsafe(kFilePath),
215                     ProvidedFileSystemInterface::OPEN_FILE_MODE_READ,
216                     base::Bind(&CallbackLogger::OnOpenFile,
217                                base::Unretained(&callback_logger)));
218  open_file.SetDispatchEventImplForTesting(
219      base::Bind(&util::LoggingDispatchEventImpl::OnDispatchEventImpl,
220                 base::Unretained(&dispatcher)));
221
222  EXPECT_TRUE(open_file.Execute(kRequestId));
223
224  open_file.OnError(kRequestId,
225                    scoped_ptr<RequestValue>(new RequestValue()),
226                    base::File::FILE_ERROR_TOO_MANY_OPENED);
227  ASSERT_EQ(1u, callback_logger.events().size());
228  CallbackLogger::Event* event = callback_logger.events()[0];
229  EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, event->result());
230  ASSERT_EQ(0, event->file_handle());
231}
232
233}  // namespace operations
234}  // namespace file_system_provider
235}  // namespace chromeos
236