OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/fileapi/syncable/local_file_sync_context.h" | 5 #include "webkit/fileapi/syncable/local_file_sync_context.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/file_path.h" | 10 #include "base/file_path.h" |
11 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
| 12 #include "base/platform_file.h" |
12 #include "base/single_thread_task_runner.h" | 13 #include "base/single_thread_task_runner.h" |
13 #include "base/threading/thread.h" | 14 #include "base/threading/thread.h" |
14 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
15 #include "webkit/fileapi/file_system_context.h" | 16 #include "webkit/fileapi/file_system_context.h" |
| 17 #include "webkit/fileapi/file_system_operation.h" |
16 #include "webkit/fileapi/isolated_context.h" | 18 #include "webkit/fileapi/isolated_context.h" |
17 #include "webkit/fileapi/syncable/canned_syncable_file_system.h" | 19 #include "webkit/fileapi/syncable/canned_syncable_file_system.h" |
18 #include "webkit/fileapi/syncable/local_file_change_tracker.h" | 20 #include "webkit/fileapi/syncable/local_file_change_tracker.h" |
19 #include "webkit/fileapi/syncable/sync_status_code.h" | 21 #include "webkit/fileapi/syncable/sync_status_code.h" |
20 #include "webkit/fileapi/syncable/syncable_file_system_util.h" | 22 #include "webkit/fileapi/syncable/syncable_file_system_util.h" |
21 | 23 |
22 // This tests LocalFileSyncContext behavior in multi-thread / | 24 // This tests LocalFileSyncContext behavior in multi-thread / |
23 // multi-file-system-context environment. | 25 // multi-file-system-context environment. |
24 // Basic combined tests (single-thread / single-file-system-context) | 26 // Basic combined tests (single-thread / single-file-system-context) |
25 // that involve LocalFileSyncContext are also in | 27 // that involve LocalFileSyncContext are also in |
26 // syncable_file_system_unittests.cc. | 28 // syncable_file_system_unittests.cc. |
27 | 29 |
28 namespace fileapi { | 30 namespace fileapi { |
29 | 31 |
30 namespace { | 32 namespace { |
31 const char kOrigin1[] = "http://example.com"; | 33 const char kOrigin1[] = "http://example.com"; |
32 const char kOrigin2[] = "http://chromium.org"; | 34 const char kOrigin2[] = "http://chromium.org"; |
33 const char kServiceName[] = "test"; | 35 const char kServiceName[] = "test"; |
34 } | 36 } |
35 | 37 |
36 class LocalFileSyncContextTest : public testing::Test { | 38 class LocalFileSyncContextTest : public testing::Test { |
37 protected: | 39 protected: |
38 LocalFileSyncContextTest() | 40 LocalFileSyncContextTest() |
39 : status_(SYNC_FILE_ERROR_FAILED) {} | 41 : status_(SYNC_FILE_ERROR_FAILED), |
| 42 file_error_(base::PLATFORM_FILE_ERROR_FAILED), |
| 43 async_modify_finished_(false), |
| 44 has_inflight_prepare_for_sync_(false) {} |
40 | 45 |
41 virtual void SetUp() OVERRIDE { | 46 virtual void SetUp() OVERRIDE { |
42 EXPECT_TRUE(fileapi::RegisterSyncableFileSystem(kServiceName)); | 47 EXPECT_TRUE(fileapi::RegisterSyncableFileSystem(kServiceName)); |
43 | 48 |
44 io_thread_.reset(new base::Thread("Thread_IO")); | 49 io_thread_.reset(new base::Thread("Thread_IO")); |
45 file_thread_.reset(new base::Thread("Thread_File")); | 50 file_thread_.reset(new base::Thread("Thread_File")); |
46 io_thread_->StartWithOptions( | 51 io_thread_->StartWithOptions( |
47 base::Thread::Options(MessageLoop::TYPE_IO, 0)); | 52 base::Thread::Options(MessageLoop::TYPE_IO, 0)); |
48 file_thread_->Start(); | 53 file_thread_->Start(); |
49 | 54 |
50 ui_task_runner_ = MessageLoop::current()->message_loop_proxy(); | 55 ui_task_runner_ = MessageLoop::current()->message_loop_proxy(); |
51 io_task_runner_ = io_thread_->message_loop_proxy(); | 56 io_task_runner_ = io_thread_->message_loop_proxy(); |
52 file_task_runner_ = file_thread_->message_loop_proxy(); | 57 file_task_runner_ = file_thread_->message_loop_proxy(); |
53 } | 58 } |
54 | 59 |
55 virtual void TearDown() OVERRIDE { | 60 virtual void TearDown() OVERRIDE { |
56 EXPECT_TRUE(fileapi::RevokeSyncableFileSystem(kServiceName)); | 61 EXPECT_TRUE(fileapi::RevokeSyncableFileSystem(kServiceName)); |
57 io_thread_->Stop(); | 62 io_thread_->Stop(); |
58 file_thread_->Stop(); | 63 file_thread_->Stop(); |
59 } | 64 } |
60 | 65 |
| 66 void StartPrepareForSync(LocalFileSyncContext* sync_context, |
| 67 const FileSystemURL& url, |
| 68 FileChangeList* changes) { |
| 69 ASSERT_TRUE(changes != NULL); |
| 70 ASSERT_FALSE(has_inflight_prepare_for_sync_); |
| 71 status_ = SYNC_STATUS_UNKNOWN; |
| 72 has_inflight_prepare_for_sync_ = true; |
| 73 sync_context->PrepareForSync( |
| 74 url, |
| 75 base::Bind(&LocalFileSyncContextTest::DidPrepareForSync, |
| 76 base::Unretained(this), changes)); |
| 77 } |
| 78 |
| 79 SyncStatusCode PrepareForSync(LocalFileSyncContext* sync_context, |
| 80 const FileSystemURL& url, |
| 81 FileChangeList* changes) { |
| 82 StartPrepareForSync(sync_context, url, changes); |
| 83 MessageLoop::current()->Run(); |
| 84 return status_; |
| 85 } |
| 86 |
| 87 base::Closure GetPrepareForSyncClosure(LocalFileSyncContext* sync_context, |
| 88 const FileSystemURL& url, |
| 89 FileChangeList* changes) { |
| 90 return base::Bind(&LocalFileSyncContextTest::StartPrepareForSync, |
| 91 base::Unretained(this), base::Unretained(sync_context), |
| 92 url, changes); |
| 93 } |
| 94 |
| 95 void DidPrepareForSync(FileChangeList* changes_out, |
| 96 SyncStatusCode status, |
| 97 const FileChangeList& changes) { |
| 98 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); |
| 99 has_inflight_prepare_for_sync_ = false; |
| 100 status_ = status; |
| 101 *changes_out = changes; |
| 102 MessageLoop::current()->Quit(); |
| 103 } |
| 104 |
| 105 void StartModifyFileOnIOThread(CannedSyncableFileSystem* file_system, |
| 106 const FileSystemURL& url) { |
| 107 async_modify_finished_ = false; |
| 108 ASSERT_TRUE(file_system != NULL); |
| 109 if (!io_task_runner_->RunsTasksOnCurrentThread()) { |
| 110 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); |
| 111 io_task_runner_->PostTask( |
| 112 FROM_HERE, |
| 113 base::Bind(&LocalFileSyncContextTest::StartModifyFileOnIOThread, |
| 114 base::Unretained(this), file_system, url)); |
| 115 return; |
| 116 } |
| 117 ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread()); |
| 118 file_error_ = base::PLATFORM_FILE_ERROR_FAILED; |
| 119 file_system->NewOperation()->Truncate( |
| 120 url, 1, base::Bind(&LocalFileSyncContextTest::DidModifyFile, |
| 121 base::Unretained(this))); |
| 122 } |
| 123 |
| 124 base::PlatformFileError WaitUntilModifyFileIsDone() { |
| 125 while (!async_modify_finished_) |
| 126 MessageLoop::current()->RunAllPending(); |
| 127 return file_error_; |
| 128 } |
| 129 |
| 130 void DidModifyFile(base::PlatformFileError error) { |
| 131 if (!ui_task_runner_->RunsTasksOnCurrentThread()) { |
| 132 ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread()); |
| 133 ui_task_runner_->PostTask( |
| 134 FROM_HERE, |
| 135 base::Bind(&LocalFileSyncContextTest::DidModifyFile, |
| 136 base::Unretained(this), error)); |
| 137 return; |
| 138 } |
| 139 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); |
| 140 file_error_ = error; |
| 141 async_modify_finished_ = true; |
| 142 } |
| 143 |
61 // These need to remain until the very end. | 144 // These need to remain until the very end. |
62 scoped_ptr<base::Thread> io_thread_; | 145 scoped_ptr<base::Thread> io_thread_; |
63 scoped_ptr<base::Thread> file_thread_; | 146 scoped_ptr<base::Thread> file_thread_; |
64 MessageLoop loop_; | 147 MessageLoop loop_; |
65 | 148 |
66 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; | 149 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; |
67 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_; | 150 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_; |
68 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; | 151 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; |
69 | 152 |
70 scoped_refptr<LocalFileSyncContext> sync_context_; | 153 scoped_refptr<LocalFileSyncContext> sync_context_; |
71 | 154 |
72 SyncStatusCode status_; | 155 SyncStatusCode status_; |
| 156 base::PlatformFileError file_error_; |
| 157 bool async_modify_finished_; |
| 158 bool has_inflight_prepare_for_sync_; |
73 }; | 159 }; |
74 | 160 |
75 TEST_F(LocalFileSyncContextTest, ConstructAndDestruct) { | 161 TEST_F(LocalFileSyncContextTest, ConstructAndDestruct) { |
76 sync_context_ = new LocalFileSyncContext( | 162 sync_context_ = new LocalFileSyncContext( |
77 ui_task_runner_, io_task_runner_); | 163 ui_task_runner_, io_task_runner_); |
78 sync_context_->ShutdownOnUIThread(); | 164 sync_context_->ShutdownOnUIThread(); |
79 } | 165 } |
80 | 166 |
81 TEST_F(LocalFileSyncContextTest, InitializeFileSystemContext) { | 167 TEST_F(LocalFileSyncContextTest, InitializeFileSystemContext) { |
82 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | 168 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
163 file_system1.file_system_context()->change_tracker()->GetChangedURLs(&urls); | 249 file_system1.file_system_context()->change_tracker()->GetChangedURLs(&urls); |
164 ASSERT_EQ(1U, urls.size()); | 250 ASSERT_EQ(1U, urls.size()); |
165 EXPECT_EQ(kURL1, urls[0]); | 251 EXPECT_EQ(kURL1, urls[0]); |
166 | 252 |
167 // file_system2's tracker now must have the change for kURL2. | 253 // file_system2's tracker now must have the change for kURL2. |
168 urls.clear(); | 254 urls.clear(); |
169 file_system2.file_system_context()->change_tracker()->GetChangedURLs(&urls); | 255 file_system2.file_system_context()->change_tracker()->GetChangedURLs(&urls); |
170 ASSERT_EQ(1U, urls.size()); | 256 ASSERT_EQ(1U, urls.size()); |
171 EXPECT_EQ(kURL2, urls[0]); | 257 EXPECT_EQ(kURL2, urls[0]); |
172 | 258 |
| 259 FileChangeList changes; |
| 260 EXPECT_EQ(SYNC_STATUS_OK, PrepareForSync(sync_context_, kURL1, &changes)); |
| 261 EXPECT_EQ(1U, changes.size()); |
| 262 EXPECT_TRUE(changes.list().back().IsFile()); |
| 263 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); |
| 264 |
| 265 changes.clear(); |
| 266 EXPECT_EQ(SYNC_STATUS_OK, PrepareForSync(sync_context_, kURL2, &changes)); |
| 267 EXPECT_EQ(1U, changes.size()); |
| 268 EXPECT_FALSE(changes.list().back().IsFile()); |
| 269 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); |
| 270 |
173 sync_context_->ShutdownOnUIThread(); | 271 sync_context_->ShutdownOnUIThread(); |
174 sync_context_ = NULL; | 272 sync_context_ = NULL; |
175 | 273 |
176 file_system1.TearDown(); | 274 file_system1.TearDown(); |
177 file_system2.TearDown(); | 275 file_system2.TearDown(); |
178 } | 276 } |
179 | 277 |
| 278 TEST_F(LocalFileSyncContextTest, PrepareSyncWhileWriting) { |
| 279 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, |
| 280 io_task_runner_); |
| 281 file_system.SetUp(); |
| 282 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); |
| 283 EXPECT_EQ(SYNC_STATUS_OK, |
| 284 file_system.MaybeInitializeFileSystemContext(sync_context_)); |
| 285 |
| 286 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); |
| 287 |
| 288 const FileSystemURL kURL1(file_system.URL("foo")); |
| 289 |
| 290 // Creates a file in file_system. |
| 291 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kURL1)); |
| 292 |
| 293 // Kick file write on IO thread. |
| 294 StartModifyFileOnIOThread(&file_system, kURL1); |
| 295 |
| 296 // Until the operation finishes PrepareForSync should return BUSY error. |
| 297 FileChangeList changes; |
| 298 EXPECT_EQ(SYNC_STATUS_FILE_BUSY, |
| 299 PrepareForSync(sync_context_, kURL1, &changes)); |
| 300 EXPECT_TRUE(changes.empty()); |
| 301 |
| 302 // Register PrepareForSync method to be invoked when kURL1 becomes |
| 303 // syncable. (Actually this may be done after all operations are done |
| 304 // on IO thread in this test.) |
| 305 sync_context_->RegisterURLForWaitingSync( |
| 306 kURL1, GetPrepareForSyncClosure(sync_context_, kURL1, &changes)); |
| 307 |
| 308 // Wait for the completion. |
| 309 EXPECT_EQ(base::PLATFORM_FILE_OK, WaitUntilModifyFileIsDone()); |
| 310 |
| 311 // The PrepareForSync must have been started; wait until DidPrepareForSync |
| 312 // is done. |
| 313 MessageLoop::current()->Run(); |
| 314 ASSERT_FALSE(has_inflight_prepare_for_sync_); |
| 315 |
| 316 // Now PrepareForSync should have run and returned OK. |
| 317 EXPECT_EQ(SYNC_STATUS_OK, status_); |
| 318 EXPECT_EQ(1U, changes.size()); |
| 319 EXPECT_TRUE(changes.list().back().IsFile()); |
| 320 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); |
| 321 |
| 322 sync_context_->ShutdownOnUIThread(); |
| 323 sync_context_ = NULL; |
| 324 |
| 325 file_system.TearDown(); |
| 326 } |
| 327 |
180 } // namespace fileapi | 328 } // namespace fileapi |
OLD | NEW |