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/drive/fileapi/fileapi_worker.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/files/file_util.h"
10#include "base/memory/scoped_ptr.h"
11#include "chrome/browser/chromeos/drive/dummy_file_system.h"
12#include "content/public/test/test_browser_thread_bundle.h"
13#include "content/public/test/test_utils.h"
14#include "google_apis/drive/test_util.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17namespace drive {
18namespace fileapi_internal {
19namespace {
20
21// Increments |num_called| for checking how many times the closure is called.
22void Increment(int* num_called) {
23  ++*num_called;
24}
25
26// Returns the |instance| as is.
27FileSystemInterface* GetFileSystem(FileSystemInterface* instance) {
28  return instance;
29}
30
31// A test file system that always returns |local_file_path|. For testing
32// purpose, it checks if |open_mode| is the expected value, and record if the
33// close callback is called.
34class TestFileSystemForOpenFile : public DummyFileSystem {
35 public:
36  TestFileSystemForOpenFile(const base::FilePath& local_file_path,
37                            OpenMode expected_open_mode)
38      : local_file_path_(local_file_path),
39        expected_open_mode_(expected_open_mode),
40        closed_(false) {
41  }
42
43  virtual void OpenFile(const base::FilePath& file_path,
44                        OpenMode open_mode,
45                        const std::string& mime_type,
46                        const drive::OpenFileCallback& callback) OVERRIDE {
47    EXPECT_EQ(expected_open_mode_, open_mode);
48
49    callback.Run(
50        FILE_ERROR_OK,
51        local_file_path_,
52        base::Bind(&TestFileSystemForOpenFile::Close, base::Unretained(this)));
53  }
54
55  void Close() {
56    closed_ = true;
57  }
58
59  bool closed() const { return closed_; }
60
61 private:
62  const base::FilePath local_file_path_;
63  const OpenMode expected_open_mode_;
64  bool closed_;
65};
66
67// Helper function of testing OpenFile() for write access. It checks that the
68// file handle correctly writes to the expected file.
69void VerifyWrite(
70    int64 expected_size,
71    const base::FilePath& expected_written_path,
72    const std::string& write_data,
73    base::File file,
74    const base::Closure& close_callback) {
75  // Check that the file was properly opened.
76  EXPECT_TRUE(file.IsValid());
77  EXPECT_FALSE(close_callback.is_null());
78
79  // Check that the file has the expected length (i.e., truncated or not)
80  base::File::Info info;
81  EXPECT_TRUE(file.GetInfo(&info));
82  EXPECT_EQ(expected_size, info.size);
83
84  // Write some data.
85  const int data_size = static_cast<int>(write_data.size());
86  EXPECT_EQ(data_size, file.Write(0, write_data.c_str(), data_size));
87  EXPECT_TRUE(file.SetLength(data_size));
88
89  // Close.
90  file.Close();
91  close_callback.Run();
92
93  // Checks that the written content goes to |expected_written_path|. I.e.,
94  // the |file| handle is pointing to the file.
95  std::string written;
96  EXPECT_TRUE(base::ReadFileToString(expected_written_path, &written));
97  EXPECT_EQ(write_data, written);
98}
99
100// Helper function of testing OpenFile() for read access. It checks that the
101// file is readable and contains |expected_data|.
102void VerifyRead(const std::string& expected_data,
103                base::File file,
104                const base::Closure& close_callback) {
105  // Check that the file was properly opened.
106  EXPECT_TRUE(file.IsValid());
107  EXPECT_FALSE(close_callback.is_null());
108
109  // Check that the file has the expected content.
110  const int data_size = static_cast<int>(expected_data.size());
111  base::File::Info info;
112  EXPECT_TRUE(file.GetInfo(&info));
113  EXPECT_EQ(data_size, info.size);
114
115  std::vector<char> buffer(data_size);
116  EXPECT_EQ(data_size, file.Read(0, buffer.data(), data_size));
117  EXPECT_EQ(expected_data, std::string(buffer.begin(), buffer.end()));
118
119  // Close.
120  file.Close();
121  close_callback.Run();
122}
123
124}  // namespace
125
126class FileApiWorkerTest : public testing::Test {
127 private:
128  content::TestBrowserThreadBundle thread_bundle_;
129};
130
131TEST_F(FileApiWorkerTest, RunFileSystemCallbackSuccess) {
132  DummyFileSystem dummy_file_system;
133
134  FileSystemInterface* file_system = NULL;
135  RunFileSystemCallback(
136      base::Bind(&GetFileSystem, &dummy_file_system),
137      google_apis::test_util::CreateCopyResultCallback(&file_system),
138      base::Closure());
139
140  EXPECT_EQ(&dummy_file_system, file_system);
141}
142
143TEST_F(FileApiWorkerTest, RunFileSystemCallbackFail) {
144  FileSystemInterface* file_system = NULL;
145
146  // Make sure on_error_callback is called if file_system_getter returns NULL.
147  int num_called = 0;
148  RunFileSystemCallback(
149      base::Bind(&GetFileSystem, static_cast<FileSystemInterface*>(NULL)),
150      google_apis::test_util::CreateCopyResultCallback(&file_system),
151      base::Bind(&Increment, &num_called));
152  EXPECT_EQ(1, num_called);
153
154  // Just make sure this null |on_error_callback| doesn't cause a crash.
155  RunFileSystemCallback(
156      base::Bind(&GetFileSystem, static_cast<FileSystemInterface*>(NULL)),
157      google_apis::test_util::CreateCopyResultCallback(&file_system),
158      base::Closure());
159}
160
161TEST_F(FileApiWorkerTest, OpenFileForCreateWrite) {
162  const base::FilePath kDummyPath = base::FilePath::FromUTF8Unsafe("whatever");
163  const std::string kWriteData = "byebye";
164
165  base::FilePath temp_path;
166  base::CreateTemporaryFile(&temp_path);
167
168  // CREATE => CREATE (fails if file exists.)
169  TestFileSystemForOpenFile file_system(temp_path, CREATE_FILE);
170  const int64 kExpectedSize = 0;
171
172  OpenFile(kDummyPath,
173           base::File::FLAG_CREATE | base::File::FLAG_WRITE,
174           base::Bind(&VerifyWrite, kExpectedSize, temp_path, kWriteData),
175           &file_system);
176  content::RunAllBlockingPoolTasksUntilIdle();
177  EXPECT_TRUE(file_system.closed());
178}
179
180TEST_F(FileApiWorkerTest, OpenFileForOpenAlwaysWrite) {
181  const base::FilePath kDummyPath = base::FilePath::FromUTF8Unsafe("whatever");
182  const std::string kWriteData = "byebye";
183  const std::string kInitialData = "hello";
184
185  base::FilePath temp_path;
186  base::CreateTemporaryFile(&temp_path);
187  google_apis::test_util::WriteStringToFile(temp_path, kInitialData);
188
189  // OPEN_ALWAYS => OPEN_OR_CREATE (success whether file exists or not.)
190  // No truncation should take place.
191  TestFileSystemForOpenFile file_system(temp_path, OPEN_OR_CREATE_FILE);
192  const int64 kExpectedSize = static_cast<int64>(kInitialData.size());
193
194  OpenFile(kDummyPath,
195           base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE,
196           base::Bind(&VerifyWrite, kExpectedSize, temp_path, kWriteData),
197           &file_system);
198  content::RunAllBlockingPoolTasksUntilIdle();
199  EXPECT_TRUE(file_system.closed());
200}
201
202TEST_F(FileApiWorkerTest, OpenFileForOpenTruncatedWrite) {
203  const base::FilePath kDummyPath = base::FilePath::FromUTF8Unsafe("whatever");
204  const std::string kInitialData = "hello";
205  const std::string kWriteData = "byebye";
206
207  base::FilePath temp_path;
208  base::CreateTemporaryFile(&temp_path);
209  google_apis::test_util::WriteStringToFile(temp_path, kInitialData);
210
211  // OPEN_TRUNCATED => OPEN (failure when the file did not exist.)
212  // It should truncate the file before passing to the callback.
213  TestFileSystemForOpenFile file_system(temp_path, OPEN_FILE);
214  const int64 kExpectedSize = 0;
215
216  OpenFile(kDummyPath,
217           base::File::FLAG_OPEN_TRUNCATED | base::File::FLAG_WRITE,
218           base::Bind(&VerifyWrite, kExpectedSize, temp_path, kWriteData),
219           &file_system);
220  content::RunAllBlockingPoolTasksUntilIdle();
221  EXPECT_TRUE(file_system.closed());
222}
223
224TEST_F(FileApiWorkerTest, OpenFileForOpenCreateAlwaysWrite) {
225  const base::FilePath kDummyPath = base::FilePath::FromUTF8Unsafe("whatever");
226  const std::string kInitialData = "hello";
227  const std::string kWriteData = "byebye";
228
229  base::FilePath temp_path;
230  base::CreateTemporaryFile(&temp_path);
231  google_apis::test_util::WriteStringToFile(temp_path, kInitialData);
232
233  // CREATE_ALWAYS => OPEN_OR_CREATE (success whether file exists or not.)
234  // It should truncate the file before passing to the callback.
235  TestFileSystemForOpenFile file_system(temp_path, OPEN_OR_CREATE_FILE);
236  const int64 kExpectedSize = 0;
237
238  OpenFile(kDummyPath,
239           base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE,
240           base::Bind(&VerifyWrite, kExpectedSize, temp_path, kWriteData),
241           &file_system);
242  content::RunAllBlockingPoolTasksUntilIdle();
243  EXPECT_TRUE(file_system.closed());
244}
245
246TEST_F(FileApiWorkerTest, OpenFileForOpenRead) {
247  const base::FilePath kDummyPath = base::FilePath::FromUTF8Unsafe("whatever");
248  const std::string kInitialData = "hello";
249
250  base::FilePath temp_path;
251  base::CreateTemporaryFile(&temp_path);
252  google_apis::test_util::WriteStringToFile(temp_path, kInitialData);
253
254  // OPEN => OPEN (failure when the file did not exist.)
255  TestFileSystemForOpenFile file_system(temp_path, OPEN_FILE);
256
257  OpenFile(kDummyPath,
258           base::File::FLAG_OPEN | base::File::FLAG_READ,
259           base::Bind(&VerifyRead, kInitialData),
260           &file_system);
261  content::RunAllBlockingPoolTasksUntilIdle();
262  EXPECT_TRUE(file_system.closed());
263}
264
265}  // namespace fileapi_internal
266}  // namespace drive
267