OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "webkit/fileapi/syncable/local_file_sync_context.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/file_util.h" | |
11 #include "base/files/file_path.h" | |
12 #include "base/message_loop.h" | |
13 #include "base/platform_file.h" | |
14 #include "base/single_thread_task_runner.h" | |
15 #include "base/stl_util.h" | |
16 #include "base/threading/thread.h" | |
17 #include "testing/gtest/include/gtest/gtest.h" | |
18 #include "webkit/blob/mock_blob_url_request_context.h" | |
19 #include "webkit/browser/fileapi/file_system_context.h" | |
20 #include "webkit/browser/fileapi/file_system_operation.h" | |
21 #include "webkit/browser/fileapi/isolated_context.h" | |
22 #include "webkit/fileapi/syncable/canned_syncable_file_system.h" | |
23 #include "webkit/fileapi/syncable/file_change.h" | |
24 #include "webkit/fileapi/syncable/local_file_change_tracker.h" | |
25 #include "webkit/fileapi/syncable/sync_file_metadata.h" | |
26 #include "webkit/fileapi/syncable/sync_status_code.h" | |
27 #include "webkit/fileapi/syncable/syncable_file_system_util.h" | |
28 | |
29 #define FPL FILE_PATH_LITERAL | |
30 | |
31 using fileapi::FileSystemContext; | |
32 using fileapi::FileSystemURL; | |
33 using fileapi::FileSystemURLSet; | |
34 | |
35 // This tests LocalFileSyncContext behavior in multi-thread / | |
36 // multi-file-system-context environment. | |
37 // Basic combined tests (single-thread / single-file-system-context) | |
38 // that involve LocalFileSyncContext are also in | |
39 // syncable_file_system_unittests.cc. | |
40 | |
41 namespace sync_file_system { | |
42 | |
43 namespace { | |
44 const char kOrigin1[] = "http://example.com"; | |
45 const char kOrigin2[] = "http://chromium.org"; | |
46 const char kServiceName[] = "test"; | |
47 } | |
48 | |
49 class LocalFileSyncContextTest : public testing::Test { | |
50 protected: | |
51 LocalFileSyncContextTest() | |
52 : status_(SYNC_FILE_ERROR_FAILED), | |
53 file_error_(base::PLATFORM_FILE_ERROR_FAILED), | |
54 async_modify_finished_(false), | |
55 has_inflight_prepare_for_sync_(false) {} | |
56 | |
57 virtual void SetUp() OVERRIDE { | |
58 EXPECT_TRUE(RegisterSyncableFileSystem(kServiceName)); | |
59 | |
60 io_thread_.reset(new base::Thread("Thread_IO")); | |
61 io_thread_->StartWithOptions( | |
62 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
63 | |
64 file_thread_.reset(new base::Thread("Thread_File")); | |
65 file_thread_->Start(); | |
66 | |
67 ui_task_runner_ = base::MessageLoop::current()->message_loop_proxy(); | |
68 io_task_runner_ = io_thread_->message_loop_proxy(); | |
69 file_task_runner_ = file_thread_->message_loop_proxy(); | |
70 } | |
71 | |
72 virtual void TearDown() OVERRIDE { | |
73 EXPECT_TRUE(RevokeSyncableFileSystem(kServiceName)); | |
74 io_thread_->Stop(); | |
75 file_thread_->Stop(); | |
76 } | |
77 | |
78 void StartPrepareForSync(FileSystemContext* file_system_context, | |
79 const FileSystemURL& url, | |
80 SyncFileMetadata* metadata, | |
81 FileChangeList* changes) { | |
82 ASSERT_TRUE(changes != NULL); | |
83 ASSERT_FALSE(has_inflight_prepare_for_sync_); | |
84 status_ = SYNC_STATUS_UNKNOWN; | |
85 has_inflight_prepare_for_sync_ = true; | |
86 sync_context_->PrepareForSync( | |
87 file_system_context, | |
88 url, | |
89 base::Bind(&LocalFileSyncContextTest::DidPrepareForSync, | |
90 base::Unretained(this), metadata, changes)); | |
91 } | |
92 | |
93 SyncStatusCode PrepareForSync(FileSystemContext* file_system_context, | |
94 const FileSystemURL& url, | |
95 SyncFileMetadata* metadata, | |
96 FileChangeList* changes) { | |
97 StartPrepareForSync(file_system_context, url, metadata, changes); | |
98 base::MessageLoop::current()->Run(); | |
99 return status_; | |
100 } | |
101 | |
102 base::Closure GetPrepareForSyncClosure(FileSystemContext* file_system_context, | |
103 const FileSystemURL& url, | |
104 SyncFileMetadata* metadata, | |
105 FileChangeList* changes) { | |
106 return base::Bind(&LocalFileSyncContextTest::StartPrepareForSync, | |
107 base::Unretained(this), | |
108 base::Unretained(file_system_context), | |
109 url, metadata, changes); | |
110 } | |
111 | |
112 void DidPrepareForSync(SyncFileMetadata* metadata_out, | |
113 FileChangeList* changes_out, | |
114 SyncStatusCode status, | |
115 const LocalFileSyncInfo& sync_file_info) { | |
116 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); | |
117 has_inflight_prepare_for_sync_ = false; | |
118 status_ = status; | |
119 *metadata_out = sync_file_info.metadata; | |
120 *changes_out = sync_file_info.changes; | |
121 base::MessageLoop::current()->Quit(); | |
122 } | |
123 | |
124 SyncStatusCode ApplyRemoteChange(FileSystemContext* file_system_context, | |
125 const FileChange& change, | |
126 const base::FilePath& local_path, | |
127 const FileSystemURL& url, | |
128 SyncFileType expected_file_type) { | |
129 SCOPED_TRACE(testing::Message() << "ApplyChange for " << | |
130 url.DebugString()); | |
131 | |
132 // First we should call PrepareForSync to disable writing. | |
133 SyncFileMetadata metadata; | |
134 FileChangeList changes; | |
135 EXPECT_EQ(SYNC_STATUS_OK, | |
136 PrepareForSync(file_system_context, url, &metadata, &changes)); | |
137 EXPECT_EQ(expected_file_type, metadata.file_type); | |
138 | |
139 status_ = SYNC_STATUS_UNKNOWN; | |
140 sync_context_->ApplyRemoteChange( | |
141 file_system_context, change, local_path, url, | |
142 base::Bind(&LocalFileSyncContextTest::DidApplyRemoteChange, | |
143 base::Unretained(this))); | |
144 base::MessageLoop::current()->Run(); | |
145 return status_; | |
146 } | |
147 | |
148 void DidApplyRemoteChange(SyncStatusCode status) { | |
149 base::MessageLoop::current()->Quit(); | |
150 status_ = status; | |
151 } | |
152 | |
153 void StartModifyFileOnIOThread(CannedSyncableFileSystem* file_system, | |
154 const FileSystemURL& url) { | |
155 async_modify_finished_ = false; | |
156 ASSERT_TRUE(file_system != NULL); | |
157 if (!io_task_runner_->RunsTasksOnCurrentThread()) { | |
158 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); | |
159 io_task_runner_->PostTask( | |
160 FROM_HERE, | |
161 base::Bind(&LocalFileSyncContextTest::StartModifyFileOnIOThread, | |
162 base::Unretained(this), file_system, url)); | |
163 return; | |
164 } | |
165 ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread()); | |
166 file_error_ = base::PLATFORM_FILE_ERROR_FAILED; | |
167 file_system->NewOperation()->Truncate( | |
168 url, 1, base::Bind(&LocalFileSyncContextTest::DidModifyFile, | |
169 base::Unretained(this))); | |
170 } | |
171 | |
172 base::PlatformFileError WaitUntilModifyFileIsDone() { | |
173 while (!async_modify_finished_) | |
174 base::MessageLoop::current()->RunUntilIdle(); | |
175 return file_error_; | |
176 } | |
177 | |
178 void DidModifyFile(base::PlatformFileError error) { | |
179 if (!ui_task_runner_->RunsTasksOnCurrentThread()) { | |
180 ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread()); | |
181 ui_task_runner_->PostTask( | |
182 FROM_HERE, | |
183 base::Bind(&LocalFileSyncContextTest::DidModifyFile, | |
184 base::Unretained(this), error)); | |
185 return; | |
186 } | |
187 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); | |
188 file_error_ = error; | |
189 async_modify_finished_ = true; | |
190 } | |
191 | |
192 // These need to remain until the very end. | |
193 scoped_ptr<base::Thread> io_thread_; | |
194 scoped_ptr<base::Thread> file_thread_; | |
195 base::MessageLoop loop_; | |
196 | |
197 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; | |
198 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; | |
199 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_; | |
200 | |
201 scoped_refptr<LocalFileSyncContext> sync_context_; | |
202 | |
203 SyncStatusCode status_; | |
204 base::PlatformFileError file_error_; | |
205 bool async_modify_finished_; | |
206 bool has_inflight_prepare_for_sync_; | |
207 }; | |
208 | |
209 TEST_F(LocalFileSyncContextTest, ConstructAndDestruct) { | |
210 sync_context_ = new LocalFileSyncContext( | |
211 ui_task_runner_, io_task_runner_); | |
212 sync_context_->ShutdownOnUIThread(); | |
213 } | |
214 | |
215 TEST_F(LocalFileSyncContextTest, InitializeFileSystemContext) { | |
216 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
217 io_task_runner_, file_task_runner_); | |
218 file_system.SetUp(); | |
219 | |
220 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
221 | |
222 // Initializes file_system using |sync_context_|. | |
223 EXPECT_EQ(SYNC_STATUS_OK, | |
224 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
225 | |
226 // Make sure everything's set up for file_system to be able to handle | |
227 // syncable file system operations. | |
228 EXPECT_TRUE(file_system.file_system_context()->sync_context() != NULL); | |
229 EXPECT_TRUE(file_system.file_system_context()->change_tracker() != NULL); | |
230 EXPECT_EQ(sync_context_.get(), | |
231 file_system.file_system_context()->sync_context()); | |
232 | |
233 // Calling MaybeInitialize for the same context multiple times must be ok. | |
234 EXPECT_EQ(SYNC_STATUS_OK, | |
235 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
236 EXPECT_EQ(sync_context_.get(), | |
237 file_system.file_system_context()->sync_context()); | |
238 | |
239 // Opens the file_system, perform some operation and see if the change tracker | |
240 // correctly captures the change. | |
241 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
242 | |
243 const FileSystemURL kURL(file_system.URL("foo")); | |
244 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kURL)); | |
245 | |
246 FileSystemURLSet urls; | |
247 file_system.GetChangedURLsInTracker(&urls); | |
248 ASSERT_EQ(1U, urls.size()); | |
249 EXPECT_TRUE(ContainsKey(urls, kURL)); | |
250 | |
251 // Finishing the test. | |
252 sync_context_->ShutdownOnUIThread(); | |
253 file_system.TearDown(); | |
254 } | |
255 | |
256 TEST_F(LocalFileSyncContextTest, MultipleFileSystemContexts) { | |
257 CannedSyncableFileSystem file_system1(GURL(kOrigin1), kServiceName, | |
258 io_task_runner_, file_task_runner_); | |
259 CannedSyncableFileSystem file_system2(GURL(kOrigin2), kServiceName, | |
260 io_task_runner_, file_task_runner_); | |
261 file_system1.SetUp(); | |
262 file_system2.SetUp(); | |
263 | |
264 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
265 | |
266 // Initializes file_system1 and file_system2. | |
267 EXPECT_EQ(SYNC_STATUS_OK, | |
268 file_system1.MaybeInitializeFileSystemContext(sync_context_)); | |
269 EXPECT_EQ(SYNC_STATUS_OK, | |
270 file_system2.MaybeInitializeFileSystemContext(sync_context_)); | |
271 | |
272 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system1.OpenFileSystem()); | |
273 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system2.OpenFileSystem()); | |
274 | |
275 const FileSystemURL kURL1(file_system1.URL("foo")); | |
276 const FileSystemURL kURL2(file_system2.URL("bar")); | |
277 | |
278 // Creates a file in file_system1. | |
279 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system1.CreateFile(kURL1)); | |
280 | |
281 // file_system1's tracker must have recorded the change. | |
282 FileSystemURLSet urls; | |
283 file_system1.GetChangedURLsInTracker(&urls); | |
284 ASSERT_EQ(1U, urls.size()); | |
285 EXPECT_TRUE(ContainsKey(urls, kURL1)); | |
286 | |
287 // file_system1's tracker must have no change. | |
288 urls.clear(); | |
289 file_system2.GetChangedURLsInTracker(&urls); | |
290 ASSERT_TRUE(urls.empty()); | |
291 | |
292 // Creates a directory in file_system2. | |
293 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system2.CreateDirectory(kURL2)); | |
294 | |
295 // file_system1's tracker must have the change for kURL1 as before. | |
296 urls.clear(); | |
297 file_system1.GetChangedURLsInTracker(&urls); | |
298 ASSERT_EQ(1U, urls.size()); | |
299 EXPECT_TRUE(ContainsKey(urls, kURL1)); | |
300 | |
301 // file_system2's tracker now must have the change for kURL2. | |
302 urls.clear(); | |
303 file_system2.GetChangedURLsInTracker(&urls); | |
304 ASSERT_EQ(1U, urls.size()); | |
305 EXPECT_TRUE(ContainsKey(urls, kURL2)); | |
306 | |
307 SyncFileMetadata metadata; | |
308 FileChangeList changes; | |
309 EXPECT_EQ(SYNC_STATUS_OK, | |
310 PrepareForSync(file_system1.file_system_context(), kURL1, | |
311 &metadata, &changes)); | |
312 EXPECT_EQ(1U, changes.size()); | |
313 EXPECT_TRUE(changes.list().back().IsFile()); | |
314 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); | |
315 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); | |
316 EXPECT_EQ(0, metadata.size); | |
317 | |
318 changes.clear(); | |
319 EXPECT_EQ(SYNC_STATUS_OK, | |
320 PrepareForSync(file_system2.file_system_context(), kURL2, | |
321 &metadata, &changes)); | |
322 EXPECT_EQ(1U, changes.size()); | |
323 EXPECT_FALSE(changes.list().back().IsFile()); | |
324 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); | |
325 EXPECT_EQ(SYNC_FILE_TYPE_DIRECTORY, metadata.file_type); | |
326 EXPECT_EQ(0, metadata.size); | |
327 | |
328 sync_context_->ShutdownOnUIThread(); | |
329 sync_context_ = NULL; | |
330 | |
331 file_system1.TearDown(); | |
332 file_system2.TearDown(); | |
333 } | |
334 | |
335 // LocalFileSyncContextTest.PrepareSyncWhileWriting is flaky on android. | |
336 // http://crbug.com/239793 | |
337 #if defined(OS_ANDROID) | |
338 #define MAYBE_PrepareSyncWhileWriting DISABLED_PrepareSyncWhileWriting | |
339 #else | |
340 #define MAYBE_PrepareSyncWhileWriting PrepareSyncWhileWriting | |
341 #endif | |
342 TEST_F(LocalFileSyncContextTest, MAYBE_PrepareSyncWhileWriting) { | |
343 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
344 io_task_runner_, file_task_runner_); | |
345 file_system.SetUp(); | |
346 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
347 EXPECT_EQ(SYNC_STATUS_OK, | |
348 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
349 | |
350 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
351 | |
352 const FileSystemURL kURL1(file_system.URL("foo")); | |
353 | |
354 // Creates a file in file_system. | |
355 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kURL1)); | |
356 | |
357 // Kick file write on IO thread. | |
358 StartModifyFileOnIOThread(&file_system, kURL1); | |
359 | |
360 // Until the operation finishes PrepareForSync should return BUSY error. | |
361 SyncFileMetadata metadata; | |
362 metadata.file_type = SYNC_FILE_TYPE_UNKNOWN; | |
363 FileChangeList changes; | |
364 EXPECT_EQ(SYNC_STATUS_FILE_BUSY, | |
365 PrepareForSync(file_system.file_system_context(), | |
366 kURL1, &metadata, &changes)); | |
367 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); | |
368 | |
369 // Register PrepareForSync method to be invoked when kURL1 becomes | |
370 // syncable. (Actually this may be done after all operations are done | |
371 // on IO thread in this test.) | |
372 metadata.file_type = SYNC_FILE_TYPE_UNKNOWN; | |
373 changes.clear(); | |
374 sync_context_->RegisterURLForWaitingSync( | |
375 kURL1, GetPrepareForSyncClosure(file_system.file_system_context(), | |
376 kURL1, &metadata, &changes)); | |
377 | |
378 // Wait for the completion. | |
379 EXPECT_EQ(base::PLATFORM_FILE_OK, WaitUntilModifyFileIsDone()); | |
380 | |
381 // The PrepareForSync must have been started; wait until DidPrepareForSync | |
382 // is done. | |
383 base::MessageLoop::current()->Run(); | |
384 ASSERT_FALSE(has_inflight_prepare_for_sync_); | |
385 | |
386 // Now PrepareForSync should have run and returned OK. | |
387 EXPECT_EQ(SYNC_STATUS_OK, status_); | |
388 EXPECT_EQ(1U, changes.size()); | |
389 EXPECT_TRUE(changes.list().back().IsFile()); | |
390 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); | |
391 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); | |
392 EXPECT_EQ(1, metadata.size); | |
393 | |
394 sync_context_->ShutdownOnUIThread(); | |
395 sync_context_ = NULL; | |
396 file_system.TearDown(); | |
397 } | |
398 | |
399 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForDeletion) { | |
400 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
401 io_task_runner_, file_task_runner_); | |
402 file_system.SetUp(); | |
403 | |
404 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
405 ASSERT_EQ(SYNC_STATUS_OK, | |
406 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
407 ASSERT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
408 | |
409 // Record the initial usage (likely 0). | |
410 int64 initial_usage = -1; | |
411 int64 quota = -1; | |
412 EXPECT_EQ(quota::kQuotaStatusOk, | |
413 file_system.GetUsageAndQuota(&initial_usage, "a)); | |
414 | |
415 // Create a file and directory in the file_system. | |
416 const FileSystemURL kFile(file_system.URL("file")); | |
417 const FileSystemURL kDir(file_system.URL("dir")); | |
418 const FileSystemURL kChild(file_system.URL("dir/child")); | |
419 | |
420 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kFile)); | |
421 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateDirectory(kDir)); | |
422 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kChild)); | |
423 | |
424 // file_system's change tracker must have recorded the creation. | |
425 FileSystemURLSet urls; | |
426 file_system.GetChangedURLsInTracker(&urls); | |
427 ASSERT_EQ(3U, urls.size()); | |
428 ASSERT_TRUE(ContainsKey(urls, kFile)); | |
429 ASSERT_TRUE(ContainsKey(urls, kDir)); | |
430 ASSERT_TRUE(ContainsKey(urls, kChild)); | |
431 for (FileSystemURLSet::iterator iter = urls.begin(); | |
432 iter != urls.end(); ++iter) { | |
433 file_system.ClearChangeForURLInTracker(*iter); | |
434 } | |
435 | |
436 // At this point the usage must be greater than the initial usage. | |
437 int64 new_usage = -1; | |
438 EXPECT_EQ(quota::kQuotaStatusOk, | |
439 file_system.GetUsageAndQuota(&new_usage, "a)); | |
440 EXPECT_GT(new_usage, initial_usage); | |
441 | |
442 // Now let's apply remote deletion changes. | |
443 FileChange change(FileChange::FILE_CHANGE_DELETE, | |
444 SYNC_FILE_TYPE_FILE); | |
445 EXPECT_EQ(SYNC_STATUS_OK, | |
446 ApplyRemoteChange(file_system.file_system_context(), | |
447 change, base::FilePath(), kFile, | |
448 SYNC_FILE_TYPE_FILE)); | |
449 | |
450 // The implementation doesn't check file type for deletion, and it must be ok | |
451 // even if we don't know if the deletion change was for a file or a directory. | |
452 change = FileChange(FileChange::FILE_CHANGE_DELETE, | |
453 SYNC_FILE_TYPE_UNKNOWN); | |
454 EXPECT_EQ(SYNC_STATUS_OK, | |
455 ApplyRemoteChange(file_system.file_system_context(), | |
456 change, base::FilePath(), kDir, | |
457 SYNC_FILE_TYPE_DIRECTORY)); | |
458 | |
459 // Check the directory/files are deleted successfully. | |
460 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
461 file_system.FileExists(kFile)); | |
462 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
463 file_system.DirectoryExists(kDir)); | |
464 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
465 file_system.FileExists(kChild)); | |
466 | |
467 // The changes applied by ApplyRemoteChange should not be recorded in | |
468 // the change tracker. | |
469 urls.clear(); | |
470 file_system.GetChangedURLsInTracker(&urls); | |
471 EXPECT_TRUE(urls.empty()); | |
472 | |
473 // The quota usage data must have reflected the deletion. | |
474 EXPECT_EQ(quota::kQuotaStatusOk, | |
475 file_system.GetUsageAndQuota(&new_usage, "a)); | |
476 EXPECT_EQ(new_usage, initial_usage); | |
477 | |
478 sync_context_->ShutdownOnUIThread(); | |
479 sync_context_ = NULL; | |
480 file_system.TearDown(); | |
481 } | |
482 | |
483 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate) { | |
484 base::ScopedTempDir temp_dir; | |
485 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
486 | |
487 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
488 io_task_runner_, file_task_runner_); | |
489 file_system.SetUp(); | |
490 | |
491 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
492 ASSERT_EQ(SYNC_STATUS_OK, | |
493 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
494 ASSERT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
495 | |
496 const FileSystemURL kFile1(file_system.URL("file1")); | |
497 const FileSystemURL kFile2(file_system.URL("file2")); | |
498 const FileSystemURL kDir(file_system.URL("dir")); | |
499 | |
500 const char kTestFileData0[] = "0123456789"; | |
501 const char kTestFileData1[] = "Lorem ipsum!"; | |
502 const char kTestFileData2[] = "This is sample test data."; | |
503 | |
504 // Create kFile1 and populate it with kTestFileData0. | |
505 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kFile1)); | |
506 EXPECT_EQ(static_cast<int64>(arraysize(kTestFileData0) - 1), | |
507 file_system.WriteString(kFile1, kTestFileData0)); | |
508 | |
509 // kFile2 and kDir are not there yet. | |
510 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
511 file_system.FileExists(kFile2)); | |
512 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
513 file_system.DirectoryExists(kDir)); | |
514 | |
515 // file_system's change tracker must have recorded the creation. | |
516 FileSystemURLSet urls; | |
517 file_system.GetChangedURLsInTracker(&urls); | |
518 ASSERT_EQ(1U, urls.size()); | |
519 EXPECT_TRUE(ContainsKey(urls, kFile1)); | |
520 file_system.ClearChangeForURLInTracker(*urls.begin()); | |
521 | |
522 // Prepare temporary files which represent the remote file data. | |
523 const base::FilePath kFilePath1(temp_dir.path().Append(FPL("file1"))); | |
524 const base::FilePath kFilePath2(temp_dir.path().Append(FPL("file2"))); | |
525 | |
526 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1) - 1), | |
527 file_util::WriteFile(kFilePath1, kTestFileData1, | |
528 arraysize(kTestFileData1) - 1)); | |
529 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1), | |
530 file_util::WriteFile(kFilePath2, kTestFileData2, | |
531 arraysize(kTestFileData2) - 1)); | |
532 | |
533 // Record the usage. | |
534 int64 usage = -1, new_usage = -1; | |
535 int64 quota = -1; | |
536 EXPECT_EQ(quota::kQuotaStatusOk, | |
537 file_system.GetUsageAndQuota(&usage, "a)); | |
538 | |
539 // Here in the local filesystem we have: | |
540 // * kFile1 with kTestFileData0 | |
541 // | |
542 // In the remote side let's assume we have: | |
543 // * kFile1 with kTestFileData1 | |
544 // * kFile2 with kTestFileData2 | |
545 // * kDir | |
546 // | |
547 // By calling ApplyChange's: | |
548 // * kFile1 will be updated to have kTestFileData1 | |
549 // * kFile2 will be created | |
550 // * kDir will be created | |
551 | |
552 // Apply the remote change to kFile1 (which will update the file). | |
553 FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
554 SYNC_FILE_TYPE_FILE); | |
555 EXPECT_EQ(SYNC_STATUS_OK, | |
556 ApplyRemoteChange(file_system.file_system_context(), | |
557 change, kFilePath1, kFile1, | |
558 SYNC_FILE_TYPE_FILE)); | |
559 | |
560 // Check if the usage has been increased by (kTestFileData1 - kTestFileData0). | |
561 const int updated_size = | |
562 arraysize(kTestFileData1) - arraysize(kTestFileData0); | |
563 EXPECT_EQ(quota::kQuotaStatusOk, | |
564 file_system.GetUsageAndQuota(&new_usage, "a)); | |
565 EXPECT_EQ(updated_size, new_usage - usage); | |
566 | |
567 // Apply remote changes to kFile2 and kDir (should create a file and | |
568 // directory respectively). | |
569 // They are non-existent yet so their expected file type (the last | |
570 // parameter of ApplyRemoteChange) are | |
571 // SYNC_FILE_TYPE_UNKNOWN. | |
572 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
573 SYNC_FILE_TYPE_FILE); | |
574 EXPECT_EQ(SYNC_STATUS_OK, | |
575 ApplyRemoteChange(file_system.file_system_context(), | |
576 change, kFilePath2, kFile2, | |
577 SYNC_FILE_TYPE_UNKNOWN)); | |
578 | |
579 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
580 SYNC_FILE_TYPE_DIRECTORY); | |
581 EXPECT_EQ(SYNC_STATUS_OK, | |
582 ApplyRemoteChange(file_system.file_system_context(), | |
583 change, base::FilePath(), kDir, | |
584 SYNC_FILE_TYPE_UNKNOWN)); | |
585 | |
586 // Calling ApplyRemoteChange with different file type should be handled as | |
587 // overwrite. | |
588 change = | |
589 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE); | |
590 EXPECT_EQ(SYNC_STATUS_OK, | |
591 ApplyRemoteChange(file_system.file_system_context(), | |
592 change, | |
593 kFilePath1, | |
594 kDir, | |
595 SYNC_FILE_TYPE_DIRECTORY)); | |
596 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.FileExists(kDir)); | |
597 | |
598 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
599 SYNC_FILE_TYPE_DIRECTORY); | |
600 EXPECT_EQ(SYNC_STATUS_OK, | |
601 ApplyRemoteChange(file_system.file_system_context(), | |
602 change, | |
603 kFilePath1, | |
604 kDir, | |
605 SYNC_FILE_TYPE_FILE)); | |
606 | |
607 // Creating a file/directory must have increased the usage more than | |
608 // the size of kTestFileData2. | |
609 new_usage = usage; | |
610 EXPECT_EQ(quota::kQuotaStatusOk, | |
611 file_system.GetUsageAndQuota(&new_usage, "a)); | |
612 EXPECT_GT(new_usage, | |
613 static_cast<int64>(usage + arraysize(kTestFileData2) - 1)); | |
614 | |
615 // The changes applied by ApplyRemoteChange should not be recorded in | |
616 // the change tracker. | |
617 urls.clear(); | |
618 file_system.GetChangedURLsInTracker(&urls); | |
619 EXPECT_TRUE(urls.empty()); | |
620 | |
621 // Make sure all three files/directory exist. | |
622 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.FileExists(kFile1)); | |
623 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.FileExists(kFile2)); | |
624 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.DirectoryExists(kDir)); | |
625 | |
626 sync_context_->ShutdownOnUIThread(); | |
627 file_system.TearDown(); | |
628 } | |
629 | |
630 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate_NoParent) { | |
631 base::ScopedTempDir temp_dir; | |
632 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
633 | |
634 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
635 io_task_runner_, file_task_runner_); | |
636 file_system.SetUp(); | |
637 | |
638 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
639 ASSERT_EQ(SYNC_STATUS_OK, | |
640 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
641 ASSERT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
642 | |
643 const char kTestFileData[] = "Lorem ipsum!"; | |
644 const FileSystemURL kDir(file_system.URL("dir")); | |
645 const FileSystemURL kFile(file_system.URL("dir/file")); | |
646 | |
647 // Either kDir or kFile not exist yet. | |
648 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_system.FileExists(kDir)); | |
649 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_system.FileExists(kFile)); | |
650 | |
651 // Prepare a temporary file which represents remote file data. | |
652 const base::FilePath kFilePath(temp_dir.path().Append(FPL("file"))); | |
653 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData) - 1), | |
654 file_util::WriteFile(kFilePath, kTestFileData, | |
655 arraysize(kTestFileData) - 1)); | |
656 | |
657 // Calling ApplyChange's with kFilePath should create | |
658 // kFile along with kDir. | |
659 FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
660 SYNC_FILE_TYPE_FILE); | |
661 EXPECT_EQ(SYNC_STATUS_OK, | |
662 ApplyRemoteChange(file_system.file_system_context(), | |
663 change, kFilePath, kFile, | |
664 SYNC_FILE_TYPE_UNKNOWN)); | |
665 | |
666 // The changes applied by ApplyRemoteChange should not be recorded in | |
667 // the change tracker. | |
668 FileSystemURLSet urls; | |
669 urls.clear(); | |
670 file_system.GetChangedURLsInTracker(&urls); | |
671 EXPECT_TRUE(urls.empty()); | |
672 | |
673 // Make sure kDir and kFile are created by ApplyRemoteChange. | |
674 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.FileExists(kFile)); | |
675 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.DirectoryExists(kDir)); | |
676 | |
677 sync_context_->ShutdownOnUIThread(); | |
678 file_system.TearDown(); | |
679 } | |
680 | |
681 } // namespace sync_file_system | |
OLD | NEW |