51f9dd7bc2ee5269f7751300ebfb4ddd5923e251
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / local / syncable_file_operation_runner_unittest.cc
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 <string>
6
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"
28
29 using fileapi::FileSystemOperation;
30 using fileapi::FileSystemURL;
31 using content::MockBlobURLRequestContext;
32 using content::ScopedTextBlob;
33 using base::File;
34
35 namespace sync_file_system {
36
37 namespace {
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";
43 }  // namespace
44
45 class SyncableFileOperationRunnerTest : public testing::Test {
46  protected:
47   typedef FileSystemOperation::StatusCallback StatusCallback;
48
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"),
55                      in_memory_env_.get(),
56                      base::MessageLoopProxy::current().get(),
57                      base::MessageLoopProxy::current().get()),
58         callback_count_(0),
59         write_status_(File::FILE_ERROR_FAILED),
60         write_bytes_(0),
61         write_complete_(false),
62         url_request_context_(file_system_.file_system_context()),
63         weak_factory_(this) {}
64
65   virtual void SetUp() OVERRIDE {
66     ASSERT_TRUE(dir_.CreateUniqueTempDir());
67
68     file_system_.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
69     sync_context_ = new LocalFileSyncContext(
70         dir_.path(),
71         in_memory_env_.get(),
72         base::MessageLoopProxy::current().get(),
73         base::MessageLoopProxy::current().get());
74     ASSERT_EQ(
75         SYNC_STATUS_OK,
76         file_system_.MaybeInitializeFileSystemContext(sync_context_.get()));
77
78     ASSERT_EQ(File::FILE_OK, file_system_.OpenFileSystem());
79     ASSERT_EQ(File::FILE_OK,
80               file_system_.CreateDirectory(URL(kParent)));
81   }
82
83   virtual void TearDown() OVERRIDE {
84     if (sync_context_.get())
85       sync_context_->ShutdownOnUIThread();
86     sync_context_ = NULL;
87
88     file_system_.TearDown();
89     RevokeSyncableFileSystem();
90   }
91
92   FileSystemURL URL(const std::string& path) {
93     return file_system_.URL(path);
94   }
95
96   LocalFileSyncStatus* sync_status() {
97     return file_system_.backend()->sync_context()->sync_status();
98   }
99
100   void ResetCallbackStatus() {
101     write_status_ = File::FILE_ERROR_FAILED;
102     write_bytes_ = 0;
103     write_complete_ = false;
104     callback_count_ = 0;
105   }
106
107   StatusCallback ExpectStatus(const tracked_objects::Location& location,
108                               File::Error expect) {
109     return base::Bind(&SyncableFileOperationRunnerTest::DidFinish,
110                       weak_factory_.GetWeakPtr(), location, expect);
111   }
112
113   FileSystemOperation::WriteCallback GetWriteCallback(
114       const tracked_objects::Location& location) {
115     return base::Bind(&SyncableFileOperationRunnerTest::DidWrite,
116                       weak_factory_.GetWeakPtr(), location);
117   }
118
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;
125     ++callback_count_;
126   }
127
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);
132     ++callback_count_;
133   }
134
135   bool CreateTempFile(base::FilePath* path) {
136     return base::CreateTemporaryFileInDir(dir_.path(), path);
137   }
138
139   ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
140   content::TestBrowserThreadBundle thread_bundle_;
141
142   base::ScopedTempDir dir_;
143   scoped_ptr<leveldb::Env> in_memory_env_;
144
145   CannedSyncableFileSystem file_system_;
146   scoped_refptr<LocalFileSyncContext> sync_context_;
147
148   int callback_count_;
149   File::Error write_status_;
150   size_t write_bytes_;
151   bool write_complete_;
152
153   MockBlobURLRequestContext url_request_context_;
154
155  private:
156   base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_;
157
158   DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest);
159 };
160
161 TEST_F(SyncableFileOperationRunnerTest, SimpleQueue) {
162   sync_status()->StartSyncing(URL(kFile));
163   ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
164
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(
171       URL(kFile), 1,
172       ExpectStatus(FROM_HERE, File::FILE_OK));
173   base::MessageLoop::current()->RunUntilIdle();
174   EXPECT_EQ(0, callback_count_);
175
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_);
181
182   // End syncing (to enable write).
183   sync_status()->EndSyncing(URL(kFile));
184   ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
185
186   ResetCallbackStatus();
187   base::MessageLoop::current()->RunUntilIdle();
188   EXPECT_EQ(2, callback_count_);
189
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_);
196 }
197
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)));
202
203   // Start syncing the kDir directory.
204   sync_status()->StartSyncing(URL(kDir));
205   ASSERT_FALSE(sync_status()->IsWritable(URL(kDir)));
206
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_);
216
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_);
222
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_);
230
231   // End syncing (to enable write).
232   sync_status()->EndSyncing(URL(kDir));
233   ASSERT_TRUE(sync_status()->IsWritable(URL(kDir)));
234
235   ResetCallbackStatus();
236   base::MessageLoop::current()->RunUntilIdle();
237   EXPECT_EQ(2, callback_count_);
238 }
239
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)));
244
245   // Start syncing the kParent directory.
246   sync_status()->StartSyncing(URL(kParent));
247
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_);
262
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")));
268
269   // Start syncing the "dest-copy2" directory.
270   sync_status()->StartSyncing(URL("dest-copy2"));
271
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_);
281
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_);
287
288   // Now we should have "dest-copy2".
289   EXPECT_EQ(File::FILE_OK,
290             file_system_.DirectoryExists(URL("dest-copy2")));
291
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_);
297
298   // Now we should have "dest-move".
299   EXPECT_EQ(File::FILE_OK,
300             file_system_.DirectoryExists(URL("dest-move")));
301 }
302
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);
307
308   sync_status()->StartSyncing(URL(kFile));
309
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_);
316
317   sync_status()->EndSyncing(URL(kFile));
318   ResetCallbackStatus();
319
320   while (!write_complete_)
321     base::MessageLoop::current()->RunUntilIdle();
322
323   EXPECT_EQ(File::FILE_OK, write_status_);
324   EXPECT_EQ(kData.size(), write_bytes_);
325   EXPECT_TRUE(write_complete_);
326 }
327
328 TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) {
329   sync_status()->StartSyncing(URL(kFile));
330   ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
331
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(
337       URL(kFile), 1,
338       ExpectStatus(FROM_HERE, File::FILE_ERROR_ABORT));
339   base::MessageLoop::current()->RunUntilIdle();
340   EXPECT_EQ(0, callback_count_);
341
342   ResetCallbackStatus();
343
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_);
349 }
350
351 // Test if CopyInForeignFile runs cooperatively with other Sync operations.
352 TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) {
353   const std::string kTestData("test data");
354
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()));
360
361   sync_status()->StartSyncing(URL(kFile));
362   ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
363
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_);
371
372   // End syncing (to enable write).
373   sync_status()->EndSyncing(URL(kFile));
374   ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
375
376   ResetCallbackStatus();
377   base::MessageLoop::current()->RunUntilIdle();
378   EXPECT_EQ(1, callback_count_);
379
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_);
387 }
388
389 TEST_F(SyncableFileOperationRunnerTest, Cancel) {
390   // Prepare a file.
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_);
396
397   // Run Truncate and immediately cancel. This shouldn't crash.
398   ResetCallbackStatus();
399   fileapi::FileSystemOperationRunner::OperationID id =
400       file_system_.operation_runner()->Truncate(
401           URL(kFile), 10,
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_);
407 }
408
409 }  // namespace sync_file_system