Upstream version 5.34.92.0
[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/test_browser_thread_bundle.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "webkit/browser/blob/mock_blob_url_request_context.h"
24 #include "webkit/browser/fileapi/file_system_context.h"
25 #include "webkit/browser/fileapi/file_system_operation_runner.h"
26
27 using fileapi::FileSystemOperation;
28 using fileapi::FileSystemURL;
29 using webkit_blob::MockBlobURLRequestContext;
30 using webkit_blob::ScopedTextBlob;
31 using base::File;
32
33 namespace sync_file_system {
34
35 namespace {
36 const std::string kParent = "foo";
37 const std::string kFile   = "foo/file";
38 const std::string kDir    = "foo/dir";
39 const std::string kChild  = "foo/dir/bar";
40 const std::string kOther  = "bar";
41 }  // namespace
42
43 class SyncableFileOperationRunnerTest : public testing::Test {
44  protected:
45   typedef FileSystemOperation::StatusCallback StatusCallback;
46
47   // Use the current thread as IO thread so that we can directly call
48   // operations in the tests.
49   SyncableFileOperationRunnerTest()
50       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
51         file_system_(GURL("http://example.com"),
52                      base::MessageLoopProxy::current().get(),
53                      base::MessageLoopProxy::current().get()),
54         callback_count_(0),
55         write_status_(File::FILE_ERROR_FAILED),
56         write_bytes_(0),
57         write_complete_(false),
58         url_request_context_(file_system_.file_system_context()),
59         weak_factory_(this) {}
60
61   virtual void SetUp() OVERRIDE {
62     ASSERT_TRUE(dir_.CreateUniqueTempDir());
63     file_system_.SetUp();
64     sync_context_ = new LocalFileSyncContext(
65         dir_.path(),
66         base::MessageLoopProxy::current().get(),
67         base::MessageLoopProxy::current().get());
68     ASSERT_EQ(
69         SYNC_STATUS_OK,
70         file_system_.MaybeInitializeFileSystemContext(sync_context_.get()));
71
72     ASSERT_EQ(File::FILE_OK, file_system_.OpenFileSystem());
73     ASSERT_EQ(File::FILE_OK,
74               file_system_.CreateDirectory(URL(kParent)));
75   }
76
77   virtual void TearDown() OVERRIDE {
78     if (sync_context_.get())
79       sync_context_->ShutdownOnUIThread();
80     sync_context_ = NULL;
81
82     file_system_.TearDown();
83     RevokeSyncableFileSystem();
84   }
85
86   FileSystemURL URL(const std::string& path) {
87     return file_system_.URL(path);
88   }
89
90   LocalFileSyncStatus* sync_status() {
91     return file_system_.backend()->sync_context()->sync_status();
92   }
93
94   void ResetCallbackStatus() {
95     write_status_ = File::FILE_ERROR_FAILED;
96     write_bytes_ = 0;
97     write_complete_ = false;
98     callback_count_ = 0;
99   }
100
101   StatusCallback ExpectStatus(const tracked_objects::Location& location,
102                               File::Error expect) {
103     return base::Bind(&SyncableFileOperationRunnerTest::DidFinish,
104                       weak_factory_.GetWeakPtr(), location, expect);
105   }
106
107   FileSystemOperation::WriteCallback GetWriteCallback(
108       const tracked_objects::Location& location) {
109     return base::Bind(&SyncableFileOperationRunnerTest::DidWrite,
110                       weak_factory_.GetWeakPtr(), location);
111   }
112
113   void DidWrite(const tracked_objects::Location& location,
114                 File::Error status, int64 bytes, bool complete) {
115     SCOPED_TRACE(testing::Message() << location.ToString());
116     write_status_ = status;
117     write_bytes_ += bytes;
118     write_complete_ = complete;
119     ++callback_count_;
120   }
121
122   void DidFinish(const tracked_objects::Location& location,
123                  File::Error expect, File::Error status) {
124     SCOPED_TRACE(testing::Message() << location.ToString());
125     EXPECT_EQ(expect, status);
126     ++callback_count_;
127   }
128
129   bool CreateTempFile(base::FilePath* path) {
130     return base::CreateTemporaryFileInDir(dir_.path(), path);
131   }
132
133   ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
134   base::ScopedTempDir dir_;
135
136   content::TestBrowserThreadBundle thread_bundle_;
137   CannedSyncableFileSystem file_system_;
138   scoped_refptr<LocalFileSyncContext> sync_context_;
139
140   int callback_count_;
141   File::Error write_status_;
142   size_t write_bytes_;
143   bool write_complete_;
144
145   MockBlobURLRequestContext url_request_context_;
146
147  private:
148   base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_;
149
150   DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest);
151 };
152
153 TEST_F(SyncableFileOperationRunnerTest, SimpleQueue) {
154   sync_status()->StartSyncing(URL(kFile));
155   ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
156
157   // The URL is in syncing so the write operations won't run.
158   ResetCallbackStatus();
159   file_system_.operation_runner()->CreateFile(
160       URL(kFile), false /* exclusive */,
161       ExpectStatus(FROM_HERE, File::FILE_OK));
162   file_system_.operation_runner()->Truncate(
163       URL(kFile), 1,
164       ExpectStatus(FROM_HERE, File::FILE_OK));
165   base::MessageLoop::current()->RunUntilIdle();
166   EXPECT_EQ(0, callback_count_);
167
168   // Read operations are not blocked (and are executed before queued ones).
169   file_system_.operation_runner()->FileExists(
170       URL(kFile), ExpectStatus(FROM_HERE, File::FILE_ERROR_NOT_FOUND));
171   base::MessageLoop::current()->RunUntilIdle();
172   EXPECT_EQ(1, callback_count_);
173
174   // End syncing (to enable write).
175   sync_status()->EndSyncing(URL(kFile));
176   ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
177
178   ResetCallbackStatus();
179   base::MessageLoop::current()->RunUntilIdle();
180   EXPECT_EQ(2, callback_count_);
181
182   // Now the file must have been created and updated.
183   ResetCallbackStatus();
184   file_system_.operation_runner()->FileExists(
185       URL(kFile), ExpectStatus(FROM_HERE, File::FILE_OK));
186   base::MessageLoop::current()->RunUntilIdle();
187   EXPECT_EQ(1, callback_count_);
188 }
189
190 TEST_F(SyncableFileOperationRunnerTest, WriteToParentAndChild) {
191   // First create the kDir directory and kChild in the dir.
192   EXPECT_EQ(File::FILE_OK, file_system_.CreateDirectory(URL(kDir)));
193   EXPECT_EQ(File::FILE_OK, file_system_.CreateFile(URL(kChild)));
194
195   // Start syncing the kDir directory.
196   sync_status()->StartSyncing(URL(kDir));
197   ASSERT_FALSE(sync_status()->IsWritable(URL(kDir)));
198
199   // Writes to kParent and kChild should be all queued up.
200   ResetCallbackStatus();
201   file_system_.operation_runner()->Truncate(
202       URL(kChild), 1, ExpectStatus(FROM_HERE, File::FILE_OK));
203   file_system_.operation_runner()->Remove(
204       URL(kParent), true /* recursive */,
205       ExpectStatus(FROM_HERE, File::FILE_OK));
206   base::MessageLoop::current()->RunUntilIdle();
207   EXPECT_EQ(0, callback_count_);
208
209   // Read operations are not blocked (and are executed before queued ones).
210   file_system_.operation_runner()->DirectoryExists(
211       URL(kDir), ExpectStatus(FROM_HERE, File::FILE_OK));
212   base::MessageLoop::current()->RunUntilIdle();
213   EXPECT_EQ(1, callback_count_);
214
215   // Writes to unrelated files must succeed as well.
216   ResetCallbackStatus();
217   file_system_.operation_runner()->CreateDirectory(
218       URL(kOther), false /* exclusive */, false /* recursive */,
219       ExpectStatus(FROM_HERE, File::FILE_OK));
220   base::MessageLoop::current()->RunUntilIdle();
221   EXPECT_EQ(1, callback_count_);
222
223   // End syncing (to enable write).
224   sync_status()->EndSyncing(URL(kDir));
225   ASSERT_TRUE(sync_status()->IsWritable(URL(kDir)));
226
227   ResetCallbackStatus();
228   base::MessageLoop::current()->RunUntilIdle();
229   EXPECT_EQ(2, callback_count_);
230 }
231
232 TEST_F(SyncableFileOperationRunnerTest, CopyAndMove) {
233   // First create the kDir directory and kChild in the dir.
234   EXPECT_EQ(File::FILE_OK, file_system_.CreateDirectory(URL(kDir)));
235   EXPECT_EQ(File::FILE_OK, file_system_.CreateFile(URL(kChild)));
236
237   // Start syncing the kParent directory.
238   sync_status()->StartSyncing(URL(kParent));
239
240   // Copying kDir to other directory should succeed, while moving would fail
241   // (since the source directory is in syncing).
242   ResetCallbackStatus();
243   file_system_.operation_runner()->Copy(
244       URL(kDir), URL("dest-copy"),
245       fileapi::FileSystemOperation::OPTION_NONE,
246       fileapi::FileSystemOperationRunner::CopyProgressCallback(),
247       ExpectStatus(FROM_HERE, File::FILE_OK));
248   file_system_.operation_runner()->Move(
249       URL(kDir), URL("dest-move"),
250       fileapi::FileSystemOperation::OPTION_NONE,
251       ExpectStatus(FROM_HERE, File::FILE_OK));
252   base::MessageLoop::current()->RunUntilIdle();
253   EXPECT_EQ(1, callback_count_);
254
255   // Only "dest-copy1" should exist.
256   EXPECT_EQ(File::FILE_OK,
257             file_system_.DirectoryExists(URL("dest-copy")));
258   EXPECT_EQ(File::FILE_ERROR_NOT_FOUND,
259             file_system_.DirectoryExists(URL("dest-move")));
260
261   // Start syncing the "dest-copy2" directory.
262   sync_status()->StartSyncing(URL("dest-copy2"));
263
264   // Now the destination is also locked copying kDir should be queued.
265   ResetCallbackStatus();
266   file_system_.operation_runner()->Copy(
267       URL(kDir), URL("dest-copy2"),
268       fileapi::FileSystemOperation::OPTION_NONE,
269       fileapi::FileSystemOperationRunner::CopyProgressCallback(),
270       ExpectStatus(FROM_HERE, File::FILE_OK));
271   base::MessageLoop::current()->RunUntilIdle();
272   EXPECT_EQ(0, callback_count_);
273
274   // Finish syncing the "dest-copy2" directory to unlock Copy.
275   sync_status()->EndSyncing(URL("dest-copy2"));
276   ResetCallbackStatus();
277   base::MessageLoop::current()->RunUntilIdle();
278   EXPECT_EQ(1, callback_count_);
279
280   // Now we should have "dest-copy2".
281   EXPECT_EQ(File::FILE_OK,
282             file_system_.DirectoryExists(URL("dest-copy2")));
283
284   // Finish syncing the kParent to unlock Move.
285   sync_status()->EndSyncing(URL(kParent));
286   ResetCallbackStatus();
287   base::MessageLoop::current()->RunUntilIdle();
288   EXPECT_EQ(1, callback_count_);
289
290   // Now we should have "dest-move".
291   EXPECT_EQ(File::FILE_OK,
292             file_system_.DirectoryExists(URL("dest-move")));
293 }
294
295 TEST_F(SyncableFileOperationRunnerTest, Write) {
296   EXPECT_EQ(File::FILE_OK, file_system_.CreateFile(URL(kFile)));
297   const std::string kData("Lorem ipsum.");
298   ScopedTextBlob blob(url_request_context_, "blob:foo", kData);
299
300   sync_status()->StartSyncing(URL(kFile));
301
302   ResetCallbackStatus();
303   file_system_.operation_runner()->Write(
304       &url_request_context_,
305       URL(kFile), blob.GetBlobDataHandle(), 0, GetWriteCallback(FROM_HERE));
306   base::MessageLoop::current()->RunUntilIdle();
307   EXPECT_EQ(0, callback_count_);
308
309   sync_status()->EndSyncing(URL(kFile));
310   ResetCallbackStatus();
311
312   while (!write_complete_)
313     base::MessageLoop::current()->RunUntilIdle();
314
315   EXPECT_EQ(File::FILE_OK, write_status_);
316   EXPECT_EQ(kData.size(), write_bytes_);
317   EXPECT_TRUE(write_complete_);
318 }
319
320 TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) {
321   sync_status()->StartSyncing(URL(kFile));
322   ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
323
324   ResetCallbackStatus();
325   file_system_.operation_runner()->CreateFile(
326       URL(kFile), false /* exclusive */,
327       ExpectStatus(FROM_HERE, File::FILE_ERROR_ABORT));
328   file_system_.operation_runner()->Truncate(
329       URL(kFile), 1,
330       ExpectStatus(FROM_HERE, File::FILE_ERROR_ABORT));
331   base::MessageLoop::current()->RunUntilIdle();
332   EXPECT_EQ(0, callback_count_);
333
334   ResetCallbackStatus();
335
336   // This shouldn't crash nor leak memory.
337   sync_context_->ShutdownOnUIThread();
338   sync_context_ = NULL;
339   base::MessageLoop::current()->RunUntilIdle();
340   EXPECT_EQ(2, callback_count_);
341 }
342
343 // Test if CopyInForeignFile runs cooperatively with other Sync operations.
344 TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) {
345   const std::string kTestData("test data");
346
347   base::FilePath temp_path;
348   ASSERT_TRUE(CreateTempFile(&temp_path));
349   ASSERT_EQ(static_cast<int>(kTestData.size()),
350             file_util::WriteFile(
351                 temp_path, kTestData.data(), kTestData.size()));
352
353   sync_status()->StartSyncing(URL(kFile));
354   ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
355
356   // The URL is in syncing so CopyIn (which is a write operation) won't run.
357   ResetCallbackStatus();
358   file_system_.operation_runner()->CopyInForeignFile(
359       temp_path, URL(kFile),
360       ExpectStatus(FROM_HERE, File::FILE_OK));
361   base::MessageLoop::current()->RunUntilIdle();
362   EXPECT_EQ(0, callback_count_);
363
364   // End syncing (to enable write).
365   sync_status()->EndSyncing(URL(kFile));
366   ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
367
368   ResetCallbackStatus();
369   base::MessageLoop::current()->RunUntilIdle();
370   EXPECT_EQ(1, callback_count_);
371
372   // Now the file must have been created and have the same content as temp_path.
373   ResetCallbackStatus();
374   file_system_.DoVerifyFile(
375       URL(kFile), kTestData,
376       ExpectStatus(FROM_HERE, File::FILE_OK));
377   base::MessageLoop::current()->RunUntilIdle();
378   EXPECT_EQ(1, callback_count_);
379 }
380
381 TEST_F(SyncableFileOperationRunnerTest, Cancel) {
382   // Prepare a file.
383   file_system_.operation_runner()->CreateFile(
384       URL(kFile), false /* exclusive */,
385       ExpectStatus(FROM_HERE, File::FILE_OK));
386   base::MessageLoop::current()->RunUntilIdle();
387   EXPECT_EQ(1, callback_count_);
388
389   // Run Truncate and immediately cancel. This shouldn't crash.
390   ResetCallbackStatus();
391   fileapi::FileSystemOperationRunner::OperationID id =
392       file_system_.operation_runner()->Truncate(
393           URL(kFile), 10,
394           ExpectStatus(FROM_HERE, File::FILE_OK));
395   file_system_.operation_runner()->Cancel(
396       id, ExpectStatus(FROM_HERE, File::FILE_ERROR_INVALID_OPERATION));
397   base::MessageLoop::current()->RunUntilIdle();
398   EXPECT_EQ(2, callback_count_);
399 }
400
401 }  // namespace sync_file_system