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