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