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/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"
27 using fileapi::FileSystemOperation;
28 using fileapi::FileSystemURL;
29 using webkit_blob::MockBlobURLRequestContext;
30 using webkit_blob::ScopedTextBlob;
33 namespace sync_file_system {
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";
43 class SyncableFileOperationRunnerTest : public testing::Test {
45 typedef FileSystemOperation::StatusCallback StatusCallback;
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()),
55 write_status_(File::FILE_ERROR_FAILED),
57 write_complete_(false),
58 url_request_context_(file_system_.file_system_context()),
59 weak_factory_(this) {}
61 virtual void SetUp() OVERRIDE {
62 ASSERT_TRUE(dir_.CreateUniqueTempDir());
64 sync_context_ = new LocalFileSyncContext(
66 base::MessageLoopProxy::current().get(),
67 base::MessageLoopProxy::current().get());
70 file_system_.MaybeInitializeFileSystemContext(sync_context_.get()));
72 ASSERT_EQ(File::FILE_OK, file_system_.OpenFileSystem());
73 ASSERT_EQ(File::FILE_OK,
74 file_system_.CreateDirectory(URL(kParent)));
77 virtual void TearDown() OVERRIDE {
78 if (sync_context_.get())
79 sync_context_->ShutdownOnUIThread();
82 file_system_.TearDown();
83 RevokeSyncableFileSystem();
86 FileSystemURL URL(const std::string& path) {
87 return file_system_.URL(path);
90 LocalFileSyncStatus* sync_status() {
91 return file_system_.backend()->sync_context()->sync_status();
94 void ResetCallbackStatus() {
95 write_status_ = File::FILE_ERROR_FAILED;
97 write_complete_ = false;
101 StatusCallback ExpectStatus(const tracked_objects::Location& location,
102 File::Error expect) {
103 return base::Bind(&SyncableFileOperationRunnerTest::DidFinish,
104 weak_factory_.GetWeakPtr(), location, expect);
107 FileSystemOperation::WriteCallback GetWriteCallback(
108 const tracked_objects::Location& location) {
109 return base::Bind(&SyncableFileOperationRunnerTest::DidWrite,
110 weak_factory_.GetWeakPtr(), location);
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;
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);
129 bool CreateTempFile(base::FilePath* path) {
130 return base::CreateTemporaryFileInDir(dir_.path(), path);
133 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
134 base::ScopedTempDir dir_;
136 content::TestBrowserThreadBundle thread_bundle_;
137 CannedSyncableFileSystem file_system_;
138 scoped_refptr<LocalFileSyncContext> sync_context_;
141 File::Error write_status_;
143 bool write_complete_;
145 MockBlobURLRequestContext url_request_context_;
148 base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_;
150 DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest);
153 TEST_F(SyncableFileOperationRunnerTest, SimpleQueue) {
154 sync_status()->StartSyncing(URL(kFile));
155 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
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(
164 ExpectStatus(FROM_HERE, File::FILE_OK));
165 base::MessageLoop::current()->RunUntilIdle();
166 EXPECT_EQ(0, callback_count_);
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_);
174 // End syncing (to enable write).
175 sync_status()->EndSyncing(URL(kFile));
176 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
178 ResetCallbackStatus();
179 base::MessageLoop::current()->RunUntilIdle();
180 EXPECT_EQ(2, callback_count_);
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_);
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)));
195 // Start syncing the kDir directory.
196 sync_status()->StartSyncing(URL(kDir));
197 ASSERT_FALSE(sync_status()->IsWritable(URL(kDir)));
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_);
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_);
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_);
223 // End syncing (to enable write).
224 sync_status()->EndSyncing(URL(kDir));
225 ASSERT_TRUE(sync_status()->IsWritable(URL(kDir)));
227 ResetCallbackStatus();
228 base::MessageLoop::current()->RunUntilIdle();
229 EXPECT_EQ(2, callback_count_);
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)));
237 // Start syncing the kParent directory.
238 sync_status()->StartSyncing(URL(kParent));
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_);
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")));
261 // Start syncing the "dest-copy2" directory.
262 sync_status()->StartSyncing(URL("dest-copy2"));
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_);
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_);
280 // Now we should have "dest-copy2".
281 EXPECT_EQ(File::FILE_OK,
282 file_system_.DirectoryExists(URL("dest-copy2")));
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_);
290 // Now we should have "dest-move".
291 EXPECT_EQ(File::FILE_OK,
292 file_system_.DirectoryExists(URL("dest-move")));
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);
300 sync_status()->StartSyncing(URL(kFile));
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_);
309 sync_status()->EndSyncing(URL(kFile));
310 ResetCallbackStatus();
312 while (!write_complete_)
313 base::MessageLoop::current()->RunUntilIdle();
315 EXPECT_EQ(File::FILE_OK, write_status_);
316 EXPECT_EQ(kData.size(), write_bytes_);
317 EXPECT_TRUE(write_complete_);
320 TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) {
321 sync_status()->StartSyncing(URL(kFile));
322 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
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(
330 ExpectStatus(FROM_HERE, File::FILE_ERROR_ABORT));
331 base::MessageLoop::current()->RunUntilIdle();
332 EXPECT_EQ(0, callback_count_);
334 ResetCallbackStatus();
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_);
343 // Test if CopyInForeignFile runs cooperatively with other Sync operations.
344 TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) {
345 const std::string kTestData("test data");
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()));
353 sync_status()->StartSyncing(URL(kFile));
354 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile)));
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_);
364 // End syncing (to enable write).
365 sync_status()->EndSyncing(URL(kFile));
366 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile)));
368 ResetCallbackStatus();
369 base::MessageLoop::current()->RunUntilIdle();
370 EXPECT_EQ(1, callback_count_);
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_);
381 TEST_F(SyncableFileOperationRunnerTest, Cancel) {
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_);
389 // Run Truncate and immediately cancel. This shouldn't crash.
390 ResetCallbackStatus();
391 fileapi::FileSystemOperationRunner::OperationID id =
392 file_system_.operation_runner()->Truncate(
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_);
401 } // namespace sync_file_system