1// Copyright 2013 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 "base/stl_util.h"
6#include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
7#include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
8#include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
9#include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
10#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
11#include "content/public/test/async_file_test_helper.h"
12#include "content/public/test/sandbox_file_system_test_helper.h"
13#include "content/public/test/test_browser_thread_bundle.h"
14#include "testing/gtest/include/gtest/gtest.h"
15#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
16#include "third_party/leveldatabase/src/include/leveldb/env.h"
17#include "webkit/browser/fileapi/file_system_context.h"
18#include "webkit/browser/fileapi/file_system_operation_context.h"
19#include "webkit/browser/fileapi/isolated_context.h"
20#include "webkit/browser/quota/quota_manager.h"
21#include "webkit/common/fileapi/file_system_types.h"
22#include "webkit/common/quota/quota_types.h"
23
24using content::SandboxFileSystemTestHelper;
25using fileapi::FileSystemContext;
26using fileapi::FileSystemOperationContext;
27using fileapi::FileSystemURL;
28using fileapi::FileSystemURLSet;
29using quota::QuotaManager;
30using quota::QuotaStatusCode;
31
32namespace sync_file_system {
33
34class SyncableFileSystemTest : public testing::Test {
35 public:
36  SyncableFileSystemTest()
37      : in_memory_env_(leveldb::NewMemEnv(leveldb::Env::Default())),
38        file_system_(GURL("http://example.com/"),
39                     in_memory_env_.get(),
40                     base::MessageLoopProxy::current().get(),
41                     base::MessageLoopProxy::current().get()),
42        weak_factory_(this) {}
43
44  virtual void SetUp() {
45    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
46    file_system_.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
47
48    sync_context_ =
49        new LocalFileSyncContext(data_dir_.path(),
50                                 in_memory_env_.get(),
51                                 base::MessageLoopProxy::current().get(),
52                                 base::MessageLoopProxy::current().get());
53    ASSERT_EQ(
54        sync_file_system::SYNC_STATUS_OK,
55        file_system_.MaybeInitializeFileSystemContext(sync_context_.get()));
56  }
57
58  virtual void TearDown() {
59    if (sync_context_.get())
60      sync_context_->ShutdownOnUIThread();
61    sync_context_ = NULL;
62
63    file_system_.TearDown();
64
65    // Make sure we don't leave the external filesystem.
66    // (CannedSyncableFileSystem::TearDown does not do this as there may be
67    // multiple syncable file systems registered for the name)
68    RevokeSyncableFileSystem();
69  }
70
71 protected:
72  void VerifyAndClearChange(const FileSystemURL& url,
73                            const FileChange& expected_change) {
74    SCOPED_TRACE(testing::Message() << url.DebugString() <<
75                 " expecting:" << expected_change.DebugString());
76    // Get the changes for URL and verify.
77    FileChangeList changes;
78    change_tracker()->GetChangesForURL(url, &changes);
79    ASSERT_EQ(1U, changes.size());
80    SCOPED_TRACE(testing::Message() << url.DebugString() <<
81                 " actual:" << changes.DebugString());
82    EXPECT_EQ(expected_change, changes.front());
83
84    // Clear the URL from the change tracker.
85    change_tracker()->ClearChangesForURL(url);
86  }
87
88  FileSystemURL URL(const std::string& path) {
89    return file_system_.URL(path);
90  }
91
92  FileSystemContext* file_system_context() {
93    return file_system_.file_system_context();
94  }
95
96  LocalFileChangeTracker* change_tracker() {
97    return file_system_.backend()->change_tracker();
98  }
99
100  ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
101
102  base::ScopedTempDir data_dir_;
103  content::TestBrowserThreadBundle thread_bundle_;
104  scoped_ptr<leveldb::Env> in_memory_env_;
105  CannedSyncableFileSystem file_system_;
106
107 private:
108  scoped_refptr<LocalFileSyncContext> sync_context_;
109
110  base::WeakPtrFactory<SyncableFileSystemTest> weak_factory_;
111
112  DISALLOW_COPY_AND_ASSIGN(SyncableFileSystemTest);
113};
114
115// Brief combined testing. Just see if all the sandbox feature works.
116TEST_F(SyncableFileSystemTest, SyncableLocalSandboxCombined) {
117  // Opens a syncable file system.
118  EXPECT_EQ(base::File::FILE_OK,
119            file_system_.OpenFileSystem());
120
121  // Do some operations.
122  EXPECT_EQ(base::File::FILE_OK,
123            file_system_.CreateDirectory(URL("dir")));
124  EXPECT_EQ(base::File::FILE_OK,
125            file_system_.CreateFile(URL("dir/foo")));
126
127  const int64 kOriginalQuota = QuotaManager::kSyncableStorageDefaultHostQuota;
128
129  const int64 kQuota = 12345 * 1024;
130  QuotaManager::kSyncableStorageDefaultHostQuota = kQuota;
131  int64 usage, quota;
132  EXPECT_EQ(quota::kQuotaStatusOk,
133            file_system_.GetUsageAndQuota(&usage, &quota));
134
135  // Returned quota must be what we overrode. Usage must be greater than 0
136  // as creating a file or directory consumes some space.
137  EXPECT_EQ(kQuota, quota);
138  EXPECT_GT(usage, 0);
139
140  // Truncate to extend an existing file and see if the usage reflects it.
141  const int64 kFileSizeToExtend = 333;
142  EXPECT_EQ(base::File::FILE_OK,
143            file_system_.CreateFile(URL("dir/foo")));
144
145  EXPECT_EQ(base::File::FILE_OK,
146            file_system_.TruncateFile(URL("dir/foo"), kFileSizeToExtend));
147
148  int64 new_usage;
149  EXPECT_EQ(quota::kQuotaStatusOk,
150            file_system_.GetUsageAndQuota(&new_usage, &quota));
151  EXPECT_EQ(kFileSizeToExtend, new_usage - usage);
152
153  // Shrink the quota to the current usage, try to extend the file further
154  // and see if it fails.
155  QuotaManager::kSyncableStorageDefaultHostQuota = new_usage;
156  EXPECT_EQ(base::File::FILE_ERROR_NO_SPACE,
157            file_system_.TruncateFile(URL("dir/foo"), kFileSizeToExtend + 1));
158
159  usage = new_usage;
160  EXPECT_EQ(quota::kQuotaStatusOk,
161            file_system_.GetUsageAndQuota(&new_usage, &quota));
162  EXPECT_EQ(usage, new_usage);
163
164  // Deletes the file system.
165  EXPECT_EQ(base::File::FILE_OK,
166            file_system_.DeleteFileSystem());
167
168  // Now the usage must be zero.
169  EXPECT_EQ(quota::kQuotaStatusOk,
170            file_system_.GetUsageAndQuota(&usage, &quota));
171  EXPECT_EQ(0, usage);
172
173  // Restore the system default quota.
174  QuotaManager::kSyncableStorageDefaultHostQuota = kOriginalQuota;
175}
176
177// Combined testing with LocalFileChangeTracker.
178TEST_F(SyncableFileSystemTest, ChangeTrackerSimple) {
179  EXPECT_EQ(base::File::FILE_OK,
180            file_system_.OpenFileSystem());
181
182  const char kPath0[] = "dir a";
183  const char kPath1[] = "dir a/dir";   // child of kPath0
184  const char kPath2[] = "dir a/file";  // child of kPath0
185  const char kPath3[] = "dir b";
186
187  // Do some operations.
188  EXPECT_EQ(base::File::FILE_OK,
189            file_system_.CreateDirectory(URL(kPath0)));  // Creates a dir.
190  EXPECT_EQ(base::File::FILE_OK,
191            file_system_.CreateDirectory(URL(kPath1)));  // Creates another.
192  EXPECT_EQ(base::File::FILE_OK,
193            file_system_.CreateFile(URL(kPath2)));       // Creates a file.
194  EXPECT_EQ(base::File::FILE_OK,
195            file_system_.TruncateFile(URL(kPath2), 1));  // Modifies the file.
196  EXPECT_EQ(base::File::FILE_OK,
197            file_system_.TruncateFile(URL(kPath2), 2));  // Modifies it again.
198
199  FileSystemURLSet urls;
200  file_system_.GetChangedURLsInTracker(&urls);
201
202  EXPECT_EQ(3U, urls.size());
203  EXPECT_TRUE(ContainsKey(urls, URL(kPath0)));
204  EXPECT_TRUE(ContainsKey(urls, URL(kPath1)));
205  EXPECT_TRUE(ContainsKey(urls, URL(kPath2)));
206
207  VerifyAndClearChange(URL(kPath0),
208                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
209                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
210  VerifyAndClearChange(URL(kPath1),
211                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
212                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
213  VerifyAndClearChange(URL(kPath2),
214                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
215                                  sync_file_system::SYNC_FILE_TYPE_FILE));
216
217  // Creates and removes a same directory.
218  EXPECT_EQ(base::File::FILE_OK,
219            file_system_.CreateDirectory(URL(kPath3)));
220  EXPECT_EQ(base::File::FILE_OK,
221            file_system_.Remove(URL(kPath3), false /* recursive */));
222
223  // The changes will be offset.
224  urls.clear();
225  file_system_.GetChangedURLsInTracker(&urls);
226  EXPECT_TRUE(urls.empty());
227
228  // Recursively removes the kPath0 directory.
229  EXPECT_EQ(base::File::FILE_OK,
230            file_system_.Remove(URL(kPath0), true /* recursive */));
231
232  urls.clear();
233  file_system_.GetChangedURLsInTracker(&urls);
234
235  // kPath0 and its all chidren (kPath1 and kPath2) must have been deleted.
236  EXPECT_EQ(3U, urls.size());
237  EXPECT_TRUE(ContainsKey(urls, URL(kPath0)));
238  EXPECT_TRUE(ContainsKey(urls, URL(kPath1)));
239  EXPECT_TRUE(ContainsKey(urls, URL(kPath2)));
240
241  VerifyAndClearChange(URL(kPath0),
242                       FileChange(FileChange::FILE_CHANGE_DELETE,
243                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
244  VerifyAndClearChange(URL(kPath1),
245                       FileChange(FileChange::FILE_CHANGE_DELETE,
246                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
247  VerifyAndClearChange(URL(kPath2),
248                       FileChange(FileChange::FILE_CHANGE_DELETE,
249                                  sync_file_system::SYNC_FILE_TYPE_FILE));
250}
251
252// Make sure directory operation is disabled (when it's configured so).
253TEST_F(SyncableFileSystemTest, DisableDirectoryOperations) {
254  ScopedDisableSyncFSV2 scoped_disable_v2;
255
256  bool was_enabled = IsSyncFSDirectoryOperationEnabled();
257  SetEnableSyncFSDirectoryOperation(false);
258  EXPECT_EQ(base::File::FILE_OK,
259            file_system_.OpenFileSystem());
260
261  // Try some directory operations (which should fail).
262  EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION,
263            file_system_.CreateDirectory(URL("dir")));
264
265  // Set up another (non-syncable) local file system.
266  SandboxFileSystemTestHelper other_file_system_(
267      GURL("http://foo.com/"), fileapi::kFileSystemTypeTemporary);
268  other_file_system_.SetUp(file_system_.file_system_context());
269
270  // Create directory '/a' and file '/a/b' in the other file system.
271  const FileSystemURL kSrcDir = other_file_system_.CreateURLFromUTF8("/a");
272  const FileSystemURL kSrcChild = other_file_system_.CreateURLFromUTF8("/a/b");
273
274  EXPECT_EQ(base::File::FILE_OK,
275            content::AsyncFileTestHelper::CreateDirectory(
276                other_file_system_.file_system_context(), kSrcDir));
277  EXPECT_EQ(base::File::FILE_OK,
278            content::AsyncFileTestHelper::CreateFile(
279                other_file_system_.file_system_context(), kSrcChild));
280
281  // Now try copying the directory into the syncable file system, which should
282  // fail if directory operation is disabled. (http://crbug.com/161442)
283  EXPECT_NE(base::File::FILE_OK,
284            file_system_.Copy(kSrcDir, URL("dest")));
285
286  other_file_system_.TearDown();
287  SetEnableSyncFSDirectoryOperation(was_enabled);
288}
289
290}  // namespace sync_file_system
291