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/fileapi/file_stream_reader.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/files/scoped_temp_dir.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/memory/weak_ptr.h"
15#include "base/run_loop.h"
16#include "chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h"
17#include "chrome/browser/chromeos/file_system_provider/service.h"
18#include "chrome/browser/chromeos/file_system_provider/service_factory.h"
19#include "chrome/test/base/testing_browser_process.h"
20#include "chrome/test/base/testing_profile.h"
21#include "chrome/test/base/testing_profile_manager.h"
22#include "content/public/test/test_browser_thread_bundle.h"
23#include "content/public/test/test_file_system_context.h"
24#include "extensions/browser/extension_registry.h"
25#include "net/base/io_buffer.h"
26#include "net/base/net_errors.h"
27#include "storage/browser/fileapi/async_file_util.h"
28#include "storage/browser/fileapi/external_mount_points.h"
29#include "storage/browser/fileapi/file_system_url.h"
30#include "testing/gtest/include/gtest/gtest.h"
31
32namespace chromeos {
33namespace file_system_provider {
34namespace {
35
36const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
37const char kFileSystemId[] = "testing-file-system";
38
39// Logs callbacks invocations on the file stream reader.
40class EventLogger {
41 public:
42  EventLogger() : weak_ptr_factory_(this) {}
43  virtual ~EventLogger() {}
44
45  void OnRead(int result) { results_.push_back(result); }
46  void OnGetLength(int64 result) { results_.push_back(result); }
47
48  base::WeakPtr<EventLogger> GetWeakPtr() {
49    return weak_ptr_factory_.GetWeakPtr();
50  }
51
52  const std::vector<int64>& results() const { return results_; }
53
54 private:
55  std::vector<int64> results_;
56  base::WeakPtrFactory<EventLogger> weak_ptr_factory_;
57
58  DISALLOW_COPY_AND_ASSIGN(EventLogger);
59};
60
61// Creates a cracked FileSystemURL for tests.
62storage::FileSystemURL CreateFileSystemURL(const std::string& mount_point_name,
63                                           const base::FilePath& file_path) {
64  const std::string origin = std::string("chrome-extension://") + kExtensionId;
65  const storage::ExternalMountPoints* const mount_points =
66      storage::ExternalMountPoints::GetSystemInstance();
67  return mount_points->CreateCrackedFileSystemURL(
68      GURL(origin),
69      storage::kFileSystemTypeExternal,
70      base::FilePath::FromUTF8Unsafe(mount_point_name).Append(file_path));
71}
72
73// Creates a Service instance. Used to be able to destroy the service in
74// TearDown().
75KeyedService* CreateService(content::BrowserContext* context) {
76  return new Service(Profile::FromBrowserContext(context),
77                     extensions::ExtensionRegistry::Get(context));
78}
79
80}  // namespace
81
82class FileSystemProviderFileStreamReader : public testing::Test {
83 protected:
84  FileSystemProviderFileStreamReader() : profile_(NULL), fake_file_(NULL) {}
85  virtual ~FileSystemProviderFileStreamReader() {}
86
87  virtual void SetUp() OVERRIDE {
88    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
89    profile_manager_.reset(
90        new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
91    ASSERT_TRUE(profile_manager_->SetUp());
92    profile_ = profile_manager_->CreateTestingProfile("testing-profile");
93
94    ServiceFactory::GetInstance()->SetTestingFactory(profile_, &CreateService);
95    Service* service = Service::Get(profile_);  // Owned by its factory.
96    service->SetFileSystemFactoryForTesting(
97        base::Bind(&FakeProvidedFileSystem::Create));
98
99    const bool result = service->MountFileSystem(kExtensionId,
100                                                 kFileSystemId,
101                                                 "Testing File System",
102                                                 false /* writable */);
103    ASSERT_TRUE(result);
104    FakeProvidedFileSystem* provided_file_system =
105        static_cast<FakeProvidedFileSystem*>(
106            service->GetProvidedFileSystem(kExtensionId, kFileSystemId));
107    ASSERT_TRUE(provided_file_system);
108    fake_file_ = provided_file_system->GetEntry(
109        base::FilePath::FromUTF8Unsafe(kFakeFilePath));
110    ASSERT_TRUE(fake_file_);
111    const ProvidedFileSystemInfo& file_system_info =
112        service->GetProvidedFileSystem(kExtensionId, kFileSystemId)
113            ->GetFileSystemInfo();
114    const std::string mount_point_name =
115        file_system_info.mount_path().BaseName().AsUTF8Unsafe();
116
117    file_url_ = CreateFileSystemURL(
118        mount_point_name, base::FilePath::FromUTF8Unsafe(kFakeFilePath + 1));
119    ASSERT_TRUE(file_url_.is_valid());
120    wrong_file_url_ = CreateFileSystemURL(
121        mount_point_name, base::FilePath::FromUTF8Unsafe("im-not-here.txt"));
122    ASSERT_TRUE(wrong_file_url_.is_valid());
123  }
124
125  virtual void TearDown() OVERRIDE {
126    // Setting the testing factory to NULL will destroy the created service
127    // associated with the testing profile.
128    ServiceFactory::GetInstance()->SetTestingFactory(profile_, NULL);
129  }
130
131  content::TestBrowserThreadBundle thread_bundle_;
132  base::ScopedTempDir data_dir_;
133  scoped_ptr<TestingProfileManager> profile_manager_;
134  TestingProfile* profile_;     // Owned by TestingProfileManager.
135  const FakeEntry* fake_file_;  // Owned by FakePRovidedFileSystem.
136  storage::FileSystemURL file_url_;
137  storage::FileSystemURL wrong_file_url_;
138};
139
140TEST_F(FileSystemProviderFileStreamReader, Read_AllAtOnce) {
141  EventLogger logger;
142
143  const int64 initial_offset = 0;
144  FileStreamReader reader(
145      NULL, file_url_, initial_offset, fake_file_->metadata->modification_time);
146  scoped_refptr<net::IOBuffer> io_buffer(
147      new net::IOBuffer(fake_file_->metadata->size));
148
149  const int result =
150      reader.Read(io_buffer.get(),
151                  fake_file_->metadata->size,
152                  base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
153  EXPECT_EQ(net::ERR_IO_PENDING, result);
154  base::RunLoop().RunUntilIdle();
155
156  ASSERT_EQ(1u, logger.results().size());
157  EXPECT_LT(0, logger.results()[0]);
158  EXPECT_EQ(fake_file_->metadata->size, logger.results()[0]);
159
160  std::string buffer_as_string(io_buffer->data(), fake_file_->metadata->size);
161  EXPECT_EQ(fake_file_->contents, buffer_as_string);
162}
163
164TEST_F(FileSystemProviderFileStreamReader, Read_WrongFile) {
165  EventLogger logger;
166
167  const int64 initial_offset = 0;
168  FileStreamReader reader(NULL,
169                          wrong_file_url_,
170                          initial_offset,
171                          fake_file_->metadata->modification_time);
172  scoped_refptr<net::IOBuffer> io_buffer(
173      new net::IOBuffer(fake_file_->metadata->size));
174
175  const int result =
176      reader.Read(io_buffer.get(),
177                  fake_file_->metadata->size,
178                  base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
179  EXPECT_EQ(net::ERR_IO_PENDING, result);
180  base::RunLoop().RunUntilIdle();
181
182  ASSERT_EQ(1u, logger.results().size());
183  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, logger.results()[0]);
184}
185
186TEST_F(FileSystemProviderFileStreamReader, Read_InChunks) {
187  EventLogger logger;
188
189  const int64 initial_offset = 0;
190  FileStreamReader reader(
191      NULL, file_url_, initial_offset, fake_file_->metadata->modification_time);
192
193  for (int64 offset = 0; offset < fake_file_->metadata->size; ++offset) {
194    scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(1));
195    const int result =
196        reader.Read(io_buffer.get(),
197                    1,
198                    base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
199    EXPECT_EQ(net::ERR_IO_PENDING, result);
200    base::RunLoop().RunUntilIdle();
201    ASSERT_EQ(offset + 1, static_cast<int64>(logger.results().size()));
202    EXPECT_EQ(1, logger.results()[offset]);
203    EXPECT_EQ(fake_file_->contents[offset], io_buffer->data()[0]);
204  }
205}
206
207TEST_F(FileSystemProviderFileStreamReader, Read_Slice) {
208  EventLogger logger;
209
210  // Trim first 3 and last 3 characters.
211  const int64 initial_offset = 3;
212  const int length = fake_file_->metadata->size - initial_offset - 3;
213  ASSERT_GT(fake_file_->metadata->size, initial_offset);
214  ASSERT_LT(0, length);
215
216  FileStreamReader reader(
217      NULL, file_url_, initial_offset, fake_file_->metadata->modification_time);
218  scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(length));
219
220  const int result =
221      reader.Read(io_buffer.get(),
222                  length,
223                  base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
224  EXPECT_EQ(net::ERR_IO_PENDING, result);
225  base::RunLoop().RunUntilIdle();
226
227  ASSERT_EQ(1u, logger.results().size());
228  EXPECT_EQ(length, logger.results()[0]);
229
230  std::string buffer_as_string(io_buffer->data(), length);
231  std::string expected_buffer(fake_file_->contents.data() + initial_offset,
232                              length);
233  EXPECT_EQ(expected_buffer, buffer_as_string);
234}
235
236TEST_F(FileSystemProviderFileStreamReader, Read_Beyond) {
237  EventLogger logger;
238
239  // Request reading 1KB more than available.
240  const int64 initial_offset = 0;
241  const int length = fake_file_->metadata->size + 1024;
242
243  FileStreamReader reader(
244      NULL, file_url_, initial_offset, fake_file_->metadata->modification_time);
245  scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(length));
246
247  const int result =
248      reader.Read(io_buffer.get(),
249                  length,
250                  base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
251  EXPECT_EQ(net::ERR_IO_PENDING, result);
252  base::RunLoop().RunUntilIdle();
253
254  ASSERT_EQ(1u, logger.results().size());
255  EXPECT_LT(0, logger.results()[0]);
256  EXPECT_EQ(fake_file_->metadata->size, logger.results()[0]);
257
258  std::string buffer_as_string(io_buffer->data(), fake_file_->metadata->size);
259  EXPECT_EQ(fake_file_->contents, buffer_as_string);
260}
261
262TEST_F(FileSystemProviderFileStreamReader, Read_ModifiedFile) {
263  EventLogger logger;
264
265  const int64 initial_offset = 0;
266  FileStreamReader reader(NULL, file_url_, initial_offset, base::Time::Max());
267
268  scoped_refptr<net::IOBuffer> io_buffer(
269      new net::IOBuffer(fake_file_->metadata->size));
270  const int result =
271      reader.Read(io_buffer.get(),
272                  fake_file_->metadata->size,
273                  base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
274
275  EXPECT_EQ(net::ERR_IO_PENDING, result);
276  base::RunLoop().RunUntilIdle();
277
278  ASSERT_EQ(1u, logger.results().size());
279  EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, logger.results()[0]);
280}
281
282TEST_F(FileSystemProviderFileStreamReader, Read_ExpectedModificationTimeNull) {
283  EventLogger logger;
284
285  const int64 initial_offset = 0;
286  FileStreamReader reader(NULL, file_url_, initial_offset, base::Time());
287
288  scoped_refptr<net::IOBuffer> io_buffer(
289      new net::IOBuffer(fake_file_->metadata->size));
290  const int result =
291      reader.Read(io_buffer.get(),
292                  fake_file_->metadata->size,
293                  base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
294
295  EXPECT_EQ(net::ERR_IO_PENDING, result);
296  base::RunLoop().RunUntilIdle();
297
298  ASSERT_EQ(1u, logger.results().size());
299  EXPECT_EQ(fake_file_->metadata->size, logger.results()[0]);
300
301  std::string buffer_as_string(io_buffer->data(), fake_file_->metadata->size);
302  EXPECT_EQ(fake_file_->contents, buffer_as_string);
303}
304
305TEST_F(FileSystemProviderFileStreamReader, GetLength) {
306  EventLogger logger;
307
308  const int64 initial_offset = 0;
309  FileStreamReader reader(
310      NULL, file_url_, initial_offset, fake_file_->metadata->modification_time);
311
312  const int result = reader.GetLength(
313      base::Bind(&EventLogger::OnGetLength, logger.GetWeakPtr()));
314  EXPECT_EQ(net::ERR_IO_PENDING, result);
315  base::RunLoop().RunUntilIdle();
316
317  ASSERT_EQ(1u, logger.results().size());
318  EXPECT_LT(0, logger.results()[0]);
319  EXPECT_EQ(fake_file_->metadata->size, logger.results()[0]);
320}
321
322TEST_F(FileSystemProviderFileStreamReader, GetLength_WrongFile) {
323  EventLogger logger;
324
325  const int64 initial_offset = 0;
326  FileStreamReader reader(NULL,
327                          wrong_file_url_,
328                          initial_offset,
329                          fake_file_->metadata->modification_time);
330
331  const int result = reader.GetLength(
332      base::Bind(&EventLogger::OnGetLength, logger.GetWeakPtr()));
333  EXPECT_EQ(net::ERR_IO_PENDING, result);
334  base::RunLoop().RunUntilIdle();
335
336  ASSERT_EQ(1u, logger.results().size());
337  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, logger.results()[0]);
338}
339
340TEST_F(FileSystemProviderFileStreamReader, GetLength_ModifiedFile) {
341  EventLogger logger;
342
343  const int64 initial_offset = 0;
344  FileStreamReader reader(NULL, file_url_, initial_offset, base::Time::Max());
345
346  const int result = reader.GetLength(
347      base::Bind(&EventLogger::OnGetLength, logger.GetWeakPtr()));
348  EXPECT_EQ(net::ERR_IO_PENDING, result);
349  base::RunLoop().RunUntilIdle();
350
351  ASSERT_EQ(1u, logger.results().size());
352  EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, logger.results()[0]);
353}
354
355TEST_F(FileSystemProviderFileStreamReader,
356       GetLength_ExpectedModificationTimeNull) {
357  EventLogger logger;
358
359  const int64 initial_offset = 0;
360  FileStreamReader reader(NULL, file_url_, initial_offset, base::Time());
361
362  const int result = reader.GetLength(
363      base::Bind(&EventLogger::OnGetLength, logger.GetWeakPtr()));
364  EXPECT_EQ(net::ERR_IO_PENDING, result);
365  base::RunLoop().RunUntilIdle();
366
367  ASSERT_EQ(1u, logger.results().size());
368  EXPECT_LT(0, logger.results()[0]);
369  EXPECT_EQ(fake_file_->metadata->size, logger.results()[0]);
370}
371
372}  // namespace file_system_provider
373}  // namespace chromeos
374