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.
7 #include "base/basictypes.h"
8 #include "base/file_util.h"
9 #include "base/files/file.h"
10 #include "base/location.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
14 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
15 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
16 #include "chrome/browser/sync_file_system/local/local_file_sync_status.h"
17 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
18 #include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h"
19 #include "chrome/browser/sync_file_system/local/syncable_file_system_operation.h"
20 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
21 #include "content/public/test/mock_blob_url_request_context.h"
22 #include "content/public/test/test_browser_thread_bundle.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
25 #include "third_party/leveldatabase/src/include/leveldb/env.h"
26 #include "webkit/browser/fileapi/file_system_context.h"
27 #include "webkit/browser/fileapi/file_system_operation_runner.h"
29 using fileapi::FileSystemOperation;
30 using fileapi::FileSystemURL;
31 using content::MockBlobURLRequestContext;
32 using content::ScopedTextBlob;
35 namespace sync_file_system {
38 const std::string kParent = "foo";
39 const std::string kFile = "foo/file";
40 const std::string kDir = "foo/dir";
41 const std::string kChild = "foo/dir/bar";
42 const std::string kOther = "bar";
45 class SyncableFileOperationRunnerTest : public testing::Test {
47 typedef FileSystemOperation::StatusCallback StatusCallback;
49 // Use the current thread as IO thread so that we can directly call
50 // operations in the tests.
51 SyncableFileOperationRunnerTest()
52 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
53 in_memory_env_(leveldb::NewMemEnv(leveldb::Env::Default())),
54 file_system_(GURL("http://example.com"),
56 base::MessageLoopProxy::current().get(),
57 base::MessageLoopProxy::current().get()),
59 write_status_(File::FILE_ERROR_FAILED),
61 write_complete_(false),
62 url_request_context_(file_system_.file_system_context()),
63 weak_factory_(this) {}
65 virtual void SetUp() OVERRIDE {
66 ASSERT_TRUE(dir_.CreateUniqueTempDir());
68 file_system_.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
69 sync_context_ = new LocalFileSyncContext(
72 base::MessageLoopProxy::current().get(),
73 base::MessageLoopProxy::current().get());
76 file_system_.MaybeInitializeFileSystemContext(sync_context_.get()));
78 ASSERT_EQ(File::FILE_OK, file_system_.OpenFileSystem());
79 ASSERT_EQ(File::FILE_OK,
80 file_system_.CreateDirectory(URL(kParent)));
83 virtual void TearDown() OVERRIDE {
84 if (sync_context_.get())
85 sync_context_->ShutdownOnUIThread();
88 file_system_.TearDown();
89 RevokeSyncableFileSystem();
92 FileSystemURL URL(const std::string& path) {
93 return file_system_.URL(path);
96 LocalFileSyncStatus* sync_status() {
97 return file_system_.backend()->sync_context()->sync_status();
100 void ResetCallbackStatus() {
101 write_status_ = File::FILE_ERROR_FAILED;
103 write_complete_ = false;
107 StatusCallback ExpectStatus(const tracked_objects::Location& location,
108 File::Error expect) {
109 return base::Bind(&SyncableFileOperationRunnerTest::DidFinish,
110 weak_factory_.GetWeakPtr(), location, expect);
113 FileSystemOperation::WriteCallback GetWriteCallback(
114 const tracked_objects::Location& location) {
115 return base::Bind(&SyncableFileOperationRunnerTest::DidWrite,
116 weak_factory_.GetWeakPtr(), location);
119 void DidWrite(const tracked_objects::Location& location,
120 File::Error status, int64 bytes, bool complete) {
121 SCOPED_TRACE(testing::Message() << location.ToString());
122 write_status_ = status;
123 write_bytes_ += bytes;
124 write_complete_ = complete;
128 void DidFinish(const tracked_objects::Location& location,
129 File::Error expect, File::Error status) {
130 SCOPED_TRACE(testing::Message() << location.ToString());
131 EXPECT_EQ(expect, status);
135 bool CreateTempFile(base::FilePath* path) {
136 return base::CreateTemporaryFileInDir(dir_.path(), path);
139 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
140 content::TestBrowserThreadBundle thread_bundle_;
142 base::ScopedTempDir dir_;
143 scoped_ptr<leveldb::Env> in_memory_env_;
145 CannedSyncableFileSystem file_system_;
146 scoped_refptr<LocalFileSyncContext> sync_context_;
149 File::Error write_status_;
151 bool write_complete_;
153 MockBlobURLRequestContext url_request_context_;
156 base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_;
158 DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest);
161 TEST_F(SyncableFileOperationRunnerTest, SimpleQueue) {
162 sync_status()->StartSyncing(URL(kFile));
163 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
165 // The URL is in syncing so the write operations won't run.
166 ResetCallbackStatus();
167 file_system_.operation_runner()->CreateFile(
168 URL(kFile), false /* exclusive */,
169 ExpectStatus(FROM_HERE, File::FILE_OK));
170 file_system_.operation_runner()->Truncate(
172 ExpectStatus(FROM_HERE, File::FILE_OK));
173 base::MessageLoop::current()->RunUntilIdle();
174 EXPECT_EQ(0, callback_count_);
176 // Read operations are not blocked (and are executed before queued ones).
177 file_system_.operation_runner()->FileExists(
178 URL(kFile), ExpectStatus(FROM_HERE, File::FILE_ERROR_NOT_FOUND));
179 base::MessageLoop::current()->RunUntilIdle();
180 EXPECT_EQ(1, callback_count_);
182 // End syncing (to enable write).
183 sync_status()->EndSyncing(URL(kFile));
184 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
186 ResetCallbackStatus();
187 base::MessageLoop::current()->RunUntilIdle();
188 EXPECT_EQ(2, callback_count_);
190 // Now the file must have been created and updated.
191 ResetCallbackStatus();
192 file_system_.operation_runner()->FileExists(
193 URL(kFile), ExpectStatus(FROM_HERE, File::FILE_OK));
194 base::MessageLoop::current()->RunUntilIdle();
195 EXPECT_EQ(1, callback_count_);
198 TEST_F(SyncableFileOperationRunnerTest, WriteToParentAndChild) {
199 // First create the kDir directory and kChild in the dir.
200 EXPECT_EQ(File::FILE_OK, file_system_.CreateDirectory(URL(kDir)));
201 EXPECT_EQ(File::FILE_OK, file_system_.CreateFile(URL(kChild)));
203 // Start syncing the kDir directory.
204 sync_status()->StartSyncing(URL(kDir));
205 ASSERT_FALSE(sync_status()->IsWritable(URL(kDir)));
207 // Writes to kParent and kChild should be all queued up.
208 ResetCallbackStatus();
209 file_system_.operation_runner()->Truncate(
210 URL(kChild), 1, ExpectStatus(FROM_HERE, File::FILE_OK));
211 file_system_.operation_runner()->Remove(
212 URL(kParent), true /* recursive */,
213 ExpectStatus(FROM_HERE, File::FILE_OK));
214 base::MessageLoop::current()->RunUntilIdle();
215 EXPECT_EQ(0, callback_count_);
217 // Read operations are not blocked (and are executed before queued ones).
218 file_system_.operation_runner()->DirectoryExists(
219 URL(kDir), ExpectStatus(FROM_HERE, File::FILE_OK));
220 base::MessageLoop::current()->RunUntilIdle();
221 EXPECT_EQ(1, callback_count_);
223 // Writes to unrelated files must succeed as well.
224 ResetCallbackStatus();
225 file_system_.operation_runner()->CreateDirectory(
226 URL(kOther), false /* exclusive */, false /* recursive */,
227 ExpectStatus(FROM_HERE, File::FILE_OK));
228 base::MessageLoop::current()->RunUntilIdle();
229 EXPECT_EQ(1, callback_count_);
231 // End syncing (to enable write).
232 sync_status()->EndSyncing(URL(kDir));
233 ASSERT_TRUE(sync_status()->IsWritable(URL(kDir)));
235 ResetCallbackStatus();
236 base::MessageLoop::current()->RunUntilIdle();
237 EXPECT_EQ(2, callback_count_);
240 TEST_F(SyncableFileOperationRunnerTest, CopyAndMove) {
241 // First create the kDir directory and kChild in the dir.
242 EXPECT_EQ(File::FILE_OK, file_system_.CreateDirectory(URL(kDir)));
243 EXPECT_EQ(File::FILE_OK, file_system_.CreateFile(URL(kChild)));
245 // Start syncing the kParent directory.
246 sync_status()->StartSyncing(URL(kParent));
248 // Copying kDir to other directory should succeed, while moving would fail
249 // (since the source directory is in syncing).
250 ResetCallbackStatus();
251 file_system_.operation_runner()->Copy(
252 URL(kDir), URL("dest-copy"),
253 fileapi::FileSystemOperation::OPTION_NONE,
254 fileapi::FileSystemOperationRunner::CopyProgressCallback(),
255 ExpectStatus(FROM_HERE, File::FILE_OK));
256 file_system_.operation_runner()->Move(
257 URL(kDir), URL("dest-move"),
258 fileapi::FileSystemOperation::OPTION_NONE,
259 ExpectStatus(FROM_HERE, File::FILE_OK));
260 base::MessageLoop::current()->RunUntilIdle();
261 EXPECT_EQ(1, callback_count_);
263 // Only "dest-copy1" should exist.
264 EXPECT_EQ(File::FILE_OK,
265 file_system_.DirectoryExists(URL("dest-copy")));
266 EXPECT_EQ(File::FILE_ERROR_NOT_FOUND,
267 file_system_.DirectoryExists(URL("dest-move")));
269 // Start syncing the "dest-copy2" directory.
270 sync_status()->StartSyncing(URL("dest-copy2"));
272 // Now the destination is also locked copying kDir should be queued.
273 ResetCallbackStatus();
274 file_system_.operation_runner()->Copy(
275 URL(kDir), URL("dest-copy2"),
276 fileapi::FileSystemOperation::OPTION_NONE,
277 fileapi::FileSystemOperationRunner::CopyProgressCallback(),
278 ExpectStatus(FROM_HERE, File::FILE_OK));
279 base::MessageLoop::current()->RunUntilIdle();
280 EXPECT_EQ(0, callback_count_);
282 // Finish syncing the "dest-copy2" directory to unlock Copy.
283 sync_status()->EndSyncing(URL("dest-copy2"));
284 ResetCallbackStatus();
285 base::MessageLoop::current()->RunUntilIdle();
286 EXPECT_EQ(1, callback_count_);
288 // Now we should have "dest-copy2".
289 EXPECT_EQ(File::FILE_OK,
290 file_system_.DirectoryExists(URL("dest-copy2")));
292 // Finish syncing the kParent to unlock Move.
293 sync_status()->EndSyncing(URL(kParent));
294 ResetCallbackStatus();
295 base::MessageLoop::current()->RunUntilIdle();
296 EXPECT_EQ(1, callback_count_);
298 // Now we should have "dest-move".
299 EXPECT_EQ(File::FILE_OK,
300 file_system_.DirectoryExists(URL("dest-move")));
303 TEST_F(SyncableFileOperationRunnerTest, Write) {
304 EXPECT_EQ(File::FILE_OK, file_system_.CreateFile(URL(kFile)));
305 const std::string kData("Lorem ipsum.");
306 ScopedTextBlob blob(url_request_context_, "blob:foo", kData);
308 sync_status()->StartSyncing(URL(kFile));
310 ResetCallbackStatus();
311 file_system_.operation_runner()->Write(
312 &url_request_context_,
313 URL(kFile), blob.GetBlobDataHandle(), 0, GetWriteCallback(FROM_HERE));
314 base::MessageLoop::current()->RunUntilIdle();
315 EXPECT_EQ(0, callback_count_);
317 sync_status()->EndSyncing(URL(kFile));
318 ResetCallbackStatus();
320 while (!write_complete_)
321 base::MessageLoop::current()->RunUntilIdle();
323 EXPECT_EQ(File::FILE_OK, write_status_);
324 EXPECT_EQ(kData.size(), write_bytes_);
325 EXPECT_TRUE(write_complete_);
328 TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) {
329 sync_status()->StartSyncing(URL(kFile));
330 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
332 ResetCallbackStatus();
333 file_system_.operation_runner()->CreateFile(
334 URL(kFile), false /* exclusive */,
335 ExpectStatus(FROM_HERE, File::FILE_ERROR_ABORT));
336 file_system_.operation_runner()->Truncate(
338 ExpectStatus(FROM_HERE, File::FILE_ERROR_ABORT));
339 base::MessageLoop::current()->RunUntilIdle();
340 EXPECT_EQ(0, callback_count_);
342 ResetCallbackStatus();
344 // This shouldn't crash nor leak memory.
345 sync_context_->ShutdownOnUIThread();
346 sync_context_ = NULL;
347 base::MessageLoop::current()->RunUntilIdle();
348 EXPECT_EQ(2, callback_count_);
351 // Test if CopyInForeignFile runs cooperatively with other Sync operations.
352 TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) {
353 const std::string kTestData("test data");
355 base::FilePath temp_path;
356 ASSERT_TRUE(CreateTempFile(&temp_path));
357 ASSERT_EQ(static_cast<int>(kTestData.size()),
358 file_util::WriteFile(
359 temp_path, kTestData.data(), kTestData.size()));
361 sync_status()->StartSyncing(URL(kFile));
362 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
364 // The URL is in syncing so CopyIn (which is a write operation) won't run.
365 ResetCallbackStatus();
366 file_system_.operation_runner()->CopyInForeignFile(
367 temp_path, URL(kFile),
368 ExpectStatus(FROM_HERE, File::FILE_OK));
369 base::MessageLoop::current()->RunUntilIdle();
370 EXPECT_EQ(0, callback_count_);
372 // End syncing (to enable write).
373 sync_status()->EndSyncing(URL(kFile));
374 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
376 ResetCallbackStatus();
377 base::MessageLoop::current()->RunUntilIdle();
378 EXPECT_EQ(1, callback_count_);
380 // Now the file must have been created and have the same content as temp_path.
381 ResetCallbackStatus();
382 file_system_.DoVerifyFile(
383 URL(kFile), kTestData,
384 ExpectStatus(FROM_HERE, File::FILE_OK));
385 base::MessageLoop::current()->RunUntilIdle();
386 EXPECT_EQ(1, callback_count_);
389 TEST_F(SyncableFileOperationRunnerTest, Cancel) {
391 file_system_.operation_runner()->CreateFile(
392 URL(kFile), false /* exclusive */,
393 ExpectStatus(FROM_HERE, File::FILE_OK));
394 base::MessageLoop::current()->RunUntilIdle();
395 EXPECT_EQ(1, callback_count_);
397 // Run Truncate and immediately cancel. This shouldn't crash.
398 ResetCallbackStatus();
399 fileapi::FileSystemOperationRunner::OperationID id =
400 file_system_.operation_runner()->Truncate(
402 ExpectStatus(FROM_HERE, File::FILE_OK));
403 file_system_.operation_runner()->Cancel(
404 id, ExpectStatus(FROM_HERE, File::FILE_ERROR_INVALID_OPERATION));
405 base::MessageLoop::current()->RunUntilIdle();
406 EXPECT_EQ(2, callback_count_);
409 } // namespace sync_file_system