local_file_sync_service_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 <vector>
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/file_util.h"
10#include "base/location.h"
11#include "base/message_loop/message_loop_proxy.h"
12#include "base/run_loop.h"
13#include "base/stl_util.h"
14#include "base/threading/thread.h"
15#include "chrome/browser/sync_file_system/file_change.h"
16#include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
17#include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
18#include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
19#include "chrome/browser/sync_file_system/local/local_file_sync_service.h"
20#include "chrome/browser/sync_file_system/local/local_file_sync_status.h"
21#include "chrome/browser/sync_file_system/local/mock_sync_status_observer.h"
22#include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
23#include "chrome/browser/sync_file_system/mock_local_change_processor.h"
24#include "chrome/browser/sync_file_system/sync_file_metadata.h"
25#include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
26#include "chrome/browser/sync_file_system/sync_status_code.h"
27#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
28#include "chrome/test/base/testing_profile.h"
29#include "content/public/browser/browser_thread.h"
30#include "content/public/test/test_browser_thread_bundle.h"
31#include "content/public/test/test_utils.h"
32#include "testing/gmock/include/gmock/gmock.h"
33#include "testing/gtest/include/gtest/gtest.h"
34#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
35#include "third_party/leveldatabase/src/include/leveldb/env.h"
36#include "webkit/browser/fileapi/file_system_context.h"
37
38using content::BrowserThread;
39using fileapi::FileSystemURL;
40using ::testing::_;
41using ::testing::AtLeast;
42using ::testing::InvokeWithoutArgs;
43using ::testing::StrictMock;
44
45namespace sync_file_system {
46
47namespace {
48
49const char kOrigin[] = "http://example.com";
50
51void DidPrepareForProcessRemoteChange(const tracked_objects::Location& where,
52                                      const base::Closure& oncompleted,
53                                      SyncStatusCode expected_status,
54                                      const SyncFileMetadata& expected_metadata,
55                                      SyncStatusCode status,
56                                      const SyncFileMetadata& metadata,
57                                      const FileChangeList& changes) {
58  SCOPED_TRACE(testing::Message() << where.ToString());
59  ASSERT_EQ(expected_status, status);
60  ASSERT_EQ(expected_metadata.file_type, metadata.file_type);
61  ASSERT_EQ(expected_metadata.size, metadata.size);
62  ASSERT_TRUE(changes.empty());
63  oncompleted.Run();
64}
65
66void OnSyncCompleted(const tracked_objects::Location& where,
67                     const base::Closure& oncompleted,
68                     SyncStatusCode expected_status,
69                     const FileSystemURL& expected_url,
70                     SyncStatusCode status,
71                     const FileSystemURL& url) {
72  SCOPED_TRACE(testing::Message() << where.ToString());
73  ASSERT_EQ(expected_status, status);
74  ASSERT_EQ(expected_url, url);
75  oncompleted.Run();
76}
77
78void OnGetFileMetadata(const tracked_objects::Location& where,
79                       const base::Closure& oncompleted,
80                       SyncStatusCode* status_out,
81                       SyncFileMetadata* metadata_out,
82                       SyncStatusCode status,
83                       const SyncFileMetadata& metadata) {
84  SCOPED_TRACE(testing::Message() << where.ToString());
85  *status_out = status;
86  *metadata_out = metadata;
87  oncompleted.Run();
88}
89
90ACTION_P(MockStatusCallback, status) {
91  base::MessageLoopProxy::current()->PostTask(
92      FROM_HERE, base::Bind(arg4, status));
93}
94
95ACTION_P2(MockStatusCallbackAndRecordChange, status, changes) {
96  base::MessageLoopProxy::current()->PostTask(
97      FROM_HERE, base::Bind(arg4, status));
98  changes->push_back(arg0);
99}
100
101}  // namespace
102
103class LocalFileSyncServiceTest
104    : public testing::Test,
105      public LocalFileSyncService::Observer {
106 protected:
107  LocalFileSyncServiceTest()
108      : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD |
109                       content::TestBrowserThreadBundle::REAL_IO_THREAD),
110        num_changes_(0) {}
111
112  virtual void SetUp() OVERRIDE {
113    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
114    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
115
116    file_system_.reset(new CannedSyncableFileSystem(
117        GURL(kOrigin),
118        in_memory_env_.get(),
119        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
120        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)));
121
122    local_service_ = LocalFileSyncService::CreateForTesting(
123        &profile_, in_memory_env_.get());
124
125    file_system_->SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
126
127    base::RunLoop run_loop;
128    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
129    local_service_->MaybeInitializeFileSystemContext(
130        GURL(kOrigin), file_system_->file_system_context(),
131        AssignAndQuitCallback(&run_loop, &status));
132    run_loop.Run();
133
134    local_service_->AddChangeObserver(this);
135
136    EXPECT_EQ(base::File::FILE_OK, file_system_->OpenFileSystem());
137
138    file_system_->backend()->sync_context()->
139        set_mock_notify_changes_duration_in_sec(0);
140  }
141
142  virtual void TearDown() OVERRIDE {
143    local_service_->Shutdown();
144    file_system_->TearDown();
145    RevokeSyncableFileSystem();
146    content::RunAllPendingInMessageLoop(BrowserThread::FILE);
147    content::RunAllPendingInMessageLoop(BrowserThread::IO);
148  }
149
150  // LocalChangeObserver overrides.
151  virtual void OnLocalChangeAvailable(int64 num_changes) OVERRIDE {
152    num_changes_ = num_changes;
153  }
154
155  void PrepareForProcessRemoteChange(
156      const FileSystemURL& url,
157      const tracked_objects::Location& where,
158      SyncStatusCode expected_status,
159      const SyncFileMetadata& expected_metadata) {
160    base::RunLoop run_loop;
161    local_service_->PrepareForProcessRemoteChange(
162        url,
163        base::Bind(&DidPrepareForProcessRemoteChange,
164                   where,
165                   run_loop.QuitClosure(),
166                   expected_status,
167                   expected_metadata));
168    run_loop.Run();
169  }
170
171  SyncStatusCode ApplyRemoteChange(const FileChange& change,
172                                   const base::FilePath& local_path,
173                                   const FileSystemURL& url) {
174    SyncStatusCode sync_status = SYNC_STATUS_UNKNOWN;
175    {
176      base::RunLoop run_loop;
177      local_service_->ApplyRemoteChange(
178          change, local_path, url,
179          AssignAndQuitCallback(&run_loop, &sync_status));
180      run_loop.Run();
181    }
182    {
183      base::RunLoop run_loop;
184      local_service_->FinalizeRemoteSync(
185          url,
186          sync_status == SYNC_STATUS_OK,
187          run_loop.QuitClosure());
188      run_loop.Run();
189    }
190    return sync_status;
191  }
192
193  int64 GetNumChangesInTracker() const {
194    return file_system_->backend()->change_tracker()->num_changes();
195  }
196
197  content::TestBrowserThreadBundle thread_bundle_;
198
199  ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
200  base::ScopedTempDir temp_dir_;
201  scoped_ptr<leveldb::Env> in_memory_env_;
202  TestingProfile profile_;
203
204  scoped_ptr<CannedSyncableFileSystem> file_system_;
205  scoped_ptr<LocalFileSyncService> local_service_;
206
207  int64 num_changes_;
208};
209
210// More complete tests for PrepareForProcessRemoteChange and ApplyRemoteChange
211// are also in content_unittest:LocalFileSyncContextTest.
212TEST_F(LocalFileSyncServiceTest, RemoteSyncStepsSimple) {
213  const FileSystemURL kFile(file_system_->URL("file"));
214  const FileSystemURL kDir(file_system_->URL("dir"));
215  const char kTestFileData[] = "0123456789";
216  const int kTestFileDataSize = static_cast<int>(arraysize(kTestFileData) - 1);
217
218  base::FilePath local_path;
219  ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &local_path));
220  ASSERT_EQ(kTestFileDataSize,
221            base::WriteFile(local_path, kTestFileData, kTestFileDataSize));
222
223  // Run PrepareForProcessRemoteChange for kFile.
224  SyncFileMetadata expected_metadata;
225  expected_metadata.file_type = SYNC_FILE_TYPE_UNKNOWN;
226  expected_metadata.size = 0;
227  PrepareForProcessRemoteChange(kFile, FROM_HERE,
228                                SYNC_STATUS_OK,
229                                expected_metadata);
230
231  // Run ApplyRemoteChange for kFile.
232  FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
233                    SYNC_FILE_TYPE_FILE);
234  EXPECT_EQ(SYNC_STATUS_OK,
235            ApplyRemoteChange(change, local_path, kFile));
236
237  // Verify the file is synced.
238  EXPECT_EQ(base::File::FILE_OK,
239            file_system_->VerifyFile(kFile, kTestFileData));
240
241  // Run PrepareForProcessRemoteChange for kDir.
242  PrepareForProcessRemoteChange(kDir, FROM_HERE,
243                                SYNC_STATUS_OK,
244                                expected_metadata);
245
246  // Run ApplyRemoteChange for kDir.
247  change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
248                      SYNC_FILE_TYPE_DIRECTORY);
249  EXPECT_EQ(SYNC_STATUS_OK,
250            ApplyRemoteChange(change, base::FilePath(), kDir));
251
252  // Verify the directory.
253  EXPECT_EQ(base::File::FILE_OK,
254            file_system_->DirectoryExists(kDir));
255
256  // Run PrepareForProcessRemoteChange and ApplyRemoteChange for
257  // kDir once again for deletion.
258  expected_metadata.file_type = SYNC_FILE_TYPE_DIRECTORY;
259  expected_metadata.size = 0;
260  PrepareForProcessRemoteChange(kDir, FROM_HERE,
261                                SYNC_STATUS_OK,
262                                expected_metadata);
263
264  change = FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN);
265  EXPECT_EQ(SYNC_STATUS_OK, ApplyRemoteChange(change, base::FilePath(), kDir));
266
267  // Now the directory must have deleted.
268  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
269            file_system_->DirectoryExists(kDir));
270}
271
272TEST_F(LocalFileSyncServiceTest, LocalChangeObserver) {
273  const FileSystemURL kFile(file_system_->URL("file"));
274  const FileSystemURL kDir(file_system_->URL("dir"));
275  const char kTestFileData[] = "0123456789";
276  const int kTestFileDataSize = static_cast<int>(arraysize(kTestFileData) - 1);
277
278  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
279
280  EXPECT_EQ(1, num_changes_);
281
282  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateDirectory(kDir));
283  EXPECT_EQ(kTestFileDataSize,
284            file_system_->WriteString(kFile, kTestFileData));
285
286  EXPECT_EQ(2, num_changes_);
287}
288
289#if defined(OS_WIN)
290// Flaky: http://crbug.com/171487
291#define MAYBE_LocalChangeObserverMultipleContexts\
292    DISABLED_LocalChangeObserverMultipleContexts
293#else
294#define MAYBE_LocalChangeObserverMultipleContexts\
295    LocalChangeObserverMultipleContexts
296#endif
297
298TEST_F(LocalFileSyncServiceTest, MAYBE_LocalChangeObserverMultipleContexts) {
299  const char kOrigin2[] = "http://foo";
300  CannedSyncableFileSystem file_system2(
301      GURL(kOrigin2),
302      in_memory_env_.get(),
303      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
304      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
305  file_system2.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
306
307  base::RunLoop run_loop;
308  SyncStatusCode status = SYNC_STATUS_UNKNOWN;
309  local_service_->MaybeInitializeFileSystemContext(
310      GURL(kOrigin2), file_system2.file_system_context(),
311      AssignAndQuitCallback(&run_loop, &status));
312  run_loop.Run();
313
314  EXPECT_EQ(base::File::FILE_OK, file_system2.OpenFileSystem());
315  file_system2.backend()->sync_context()->
316      set_mock_notify_changes_duration_in_sec(0);
317
318  const FileSystemURL kFile1(file_system_->URL("file1"));
319  const FileSystemURL kFile2(file_system_->URL("file2"));
320  const FileSystemURL kFile3(file_system2.URL("file3"));
321  const FileSystemURL kFile4(file_system2.URL("file4"));
322
323  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile1));
324  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile2));
325  EXPECT_EQ(base::File::FILE_OK, file_system2.CreateFile(kFile3));
326  EXPECT_EQ(base::File::FILE_OK, file_system2.CreateFile(kFile4));
327
328  EXPECT_EQ(4, num_changes_);
329
330  file_system2.TearDown();
331}
332
333TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_CreateFile) {
334  const FileSystemURL kFile(file_system_->URL("foo"));
335  const char kTestFileData[] = "0123456789";
336  const int kTestFileDataSize = static_cast<int>(arraysize(kTestFileData) - 1);
337
338  base::RunLoop run_loop;
339
340  // We should get called OnSyncEnabled and OnWriteEnabled on kFile.
341  // (OnWriteEnabled is called because we release lock before returning
342  // from ApplyLocalChange)
343  StrictMock<MockSyncStatusObserver> status_observer;
344  EXPECT_CALL(status_observer, OnSyncEnabled(kFile)).Times(AtLeast(1));
345  EXPECT_CALL(status_observer, OnWriteEnabled(kFile)).Times(AtLeast(0));
346  file_system_->AddSyncStatusObserver(&status_observer);
347
348  // Creates and writes into a file.
349  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
350  EXPECT_EQ(kTestFileDataSize,
351            file_system_->WriteString(kFile, std::string(kTestFileData)));
352
353  // Retrieve the expected file info.
354  base::File::Info info;
355  base::FilePath platform_path;
356  EXPECT_EQ(base::File::FILE_OK,
357            file_system_->GetMetadataAndPlatformPath(
358                kFile, &info, &platform_path));
359
360  ASSERT_FALSE(info.is_directory);
361  ASSERT_EQ(kTestFileDataSize, info.size);
362
363  SyncFileMetadata metadata;
364  metadata.file_type = SYNC_FILE_TYPE_FILE;
365  metadata.size = info.size;
366  metadata.last_modified = info.last_modified;
367
368  // The local_change_processor's ApplyLocalChange should be called once
369  // with ADD_OR_UPDATE change for TYPE_FILE.
370  StrictMock<MockLocalChangeProcessor> local_change_processor;
371  const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
372                          SYNC_FILE_TYPE_FILE);
373  EXPECT_CALL(local_change_processor,
374              ApplyLocalChange(change, _, metadata, kFile, _))
375      .WillOnce(MockStatusCallback(SYNC_STATUS_OK));
376
377  local_service_->SetLocalChangeProcessor(&local_change_processor);
378  local_service_->ProcessLocalChange(
379      base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
380                 SYNC_STATUS_OK, kFile));
381
382  run_loop.Run();
383
384  file_system_->RemoveSyncStatusObserver(&status_observer);
385
386  EXPECT_EQ(0, GetNumChangesInTracker());
387}
388
389TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_CreateAndRemoveFile) {
390  const FileSystemURL kFile(file_system_->URL("foo"));
391
392  base::RunLoop run_loop;
393
394  // We should get called OnSyncEnabled and possibly OnWriteEnabled (depends
395  // on timing) on kFile.
396  StrictMock<MockSyncStatusObserver> status_observer;
397  EXPECT_CALL(status_observer, OnSyncEnabled(kFile)).Times(AtLeast(1));
398  EXPECT_CALL(status_observer, OnWriteEnabled(kFile)).Times(AtLeast(0));
399  file_system_->AddSyncStatusObserver(&status_observer);
400
401  // Creates and then deletes a file.
402  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
403  EXPECT_EQ(base::File::FILE_OK, file_system_->Remove(kFile, false));
404
405  // The local_change_processor's ApplyLocalChange should be called once
406  // with DELETE change for TYPE_FILE.
407  // The file will NOT exist in the remote side and the processor might
408  // return SYNC_FILE_ERROR_NOT_FOUND (as mocked).
409  StrictMock<MockLocalChangeProcessor> local_change_processor;
410  const FileChange change(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_FILE);
411  EXPECT_CALL(local_change_processor, ApplyLocalChange(change, _, _, kFile, _))
412      .WillOnce(MockStatusCallback(SYNC_FILE_ERROR_NOT_FOUND));
413
414  // The sync should succeed anyway.
415  local_service_->SetLocalChangeProcessor(&local_change_processor);
416  local_service_->ProcessLocalChange(
417      base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
418                 SYNC_STATUS_OK, kFile));
419
420  run_loop.Run();
421
422  file_system_->RemoveSyncStatusObserver(&status_observer);
423
424  EXPECT_EQ(0, GetNumChangesInTracker());
425}
426
427TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_CreateAndRemoveDirectory) {
428  const FileSystemURL kDir(file_system_->URL("foo"));
429
430  base::RunLoop run_loop;
431
432  // OnSyncEnabled is expected to be called at least or more than once.
433  StrictMock<MockSyncStatusObserver> status_observer;
434  EXPECT_CALL(status_observer, OnSyncEnabled(kDir)).Times(AtLeast(1));
435  file_system_->AddSyncStatusObserver(&status_observer);
436
437  // Creates and then deletes a directory.
438  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateDirectory(kDir));
439  EXPECT_EQ(base::File::FILE_OK, file_system_->Remove(kDir, false));
440
441  // The local_change_processor's ApplyLocalChange should never be called.
442  StrictMock<MockLocalChangeProcessor> local_change_processor;
443
444  local_service_->SetLocalChangeProcessor(&local_change_processor);
445  local_service_->ProcessLocalChange(
446      base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
447                 SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL()));
448
449  run_loop.Run();
450
451  file_system_->RemoveSyncStatusObserver(&status_observer);
452
453  EXPECT_EQ(0, GetNumChangesInTracker());
454}
455
456TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_MultipleChanges) {
457  const FileSystemURL kPath(file_system_->URL("foo"));
458  const FileSystemURL kOther(file_system_->URL("bar"));
459
460  base::RunLoop run_loop;
461
462  // We should get called OnSyncEnabled and OnWriteEnabled on kPath and
463  // OnSyncEnabled on kOther.
464  StrictMock<MockSyncStatusObserver> status_observer;
465  EXPECT_CALL(status_observer, OnSyncEnabled(kPath)).Times(AtLeast(1));
466  EXPECT_CALL(status_observer, OnSyncEnabled(kOther)).Times(AtLeast(1));
467  file_system_->AddSyncStatusObserver(&status_observer);
468
469  // Creates a file, delete the file and creates a directory with the same
470  // name.
471  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kPath));
472  EXPECT_EQ(base::File::FILE_OK, file_system_->Remove(kPath, false));
473  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateDirectory(kPath));
474
475  // Creates one more file.
476  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kOther));
477
478  // The local_change_processor's ApplyLocalChange will be called
479  // twice for FILE_TYPE and FILE_DIRECTORY.
480  StrictMock<MockLocalChangeProcessor> local_change_processor;
481  std::vector<FileChange> changes;
482  EXPECT_CALL(local_change_processor, ApplyLocalChange(_, _, _, kPath, _))
483      .Times(2)
484      .WillOnce(MockStatusCallbackAndRecordChange(SYNC_STATUS_OK, &changes))
485      .WillOnce(MockStatusCallbackAndRecordChange(SYNC_STATUS_OK, &changes));
486  local_service_->SetLocalChangeProcessor(&local_change_processor);
487
488  // OnWriteEnabled will be notified on kPath (in multi-threaded this
489  // could be delayed, so AtLeast(0)).
490  EXPECT_CALL(status_observer, OnWriteEnabled(kPath)).Times(AtLeast(0));
491
492  local_service_->ProcessLocalChange(
493      base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
494                 SYNC_STATUS_OK, kPath));
495
496  run_loop.Run();
497
498  EXPECT_EQ(2U, changes.size());
499  EXPECT_EQ(FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_FILE),
500            changes[0]);
501  EXPECT_EQ(FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
502                       SYNC_FILE_TYPE_DIRECTORY),
503            changes[1]);
504
505  file_system_->RemoveSyncStatusObserver(&status_observer);
506
507  // We have one more change for kOther.
508  EXPECT_EQ(1, GetNumChangesInTracker());
509}
510
511TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_GetLocalMetadata) {
512  const FileSystemURL kURL(file_system_->URL("foo"));
513  const base::Time kTime = base::Time::FromDoubleT(333);
514  const int kSize = 555;
515
516  base::RunLoop run_loop;
517
518  // Creates a file.
519  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kURL));
520  EXPECT_EQ(base::File::FILE_OK, file_system_->TruncateFile(kURL, kSize));
521  EXPECT_EQ(base::File::FILE_OK,
522            file_system_->TouchFile(kURL, base::Time(), kTime));
523
524  SyncStatusCode status = SYNC_STATUS_UNKNOWN;
525  SyncFileMetadata metadata;
526  local_service_->GetLocalFileMetadata(
527      kURL,
528      base::Bind(&OnGetFileMetadata, FROM_HERE, run_loop.QuitClosure(),
529                 &status, &metadata));
530
531  run_loop.Run();
532
533  EXPECT_EQ(SYNC_STATUS_OK, status);
534  EXPECT_EQ(kTime, metadata.last_modified);
535  EXPECT_EQ(kSize, metadata.size);
536}
537
538TEST_F(LocalFileSyncServiceTest, RecordFakeChange) {
539  const FileSystemURL kURL(file_system_->URL("foo"));
540
541  // Create a file and reset the changes (as preparation).
542  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kURL));
543  file_system_->ClearChangeForURLInTracker(kURL);
544
545  EXPECT_EQ(0, GetNumChangesInTracker());
546
547  fileapi::FileSystemURLSet urlset;
548  file_system_->GetChangedURLsInTracker(&urlset);
549  EXPECT_TRUE(urlset.empty());
550
551  const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
552                          SYNC_FILE_TYPE_FILE);
553
554  // Call RecordFakeLocalChange to add an ADD_OR_UPDATE change.
555  {
556    base::RunLoop run_loop;
557    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
558    local_service_->RecordFakeLocalChange(
559        kURL, change, AssignAndQuitCallback(&run_loop, &status));
560    run_loop.Run();
561    EXPECT_EQ(SYNC_STATUS_OK, status);
562  }
563
564  EXPECT_EQ(1, GetNumChangesInTracker());
565  file_system_->GetChangedURLsInTracker(&urlset);
566  EXPECT_EQ(1U, urlset.size());
567  EXPECT_TRUE(urlset.find(kURL) != urlset.end());
568
569  // Next local sync should pick up the recorded change.
570  StrictMock<MockLocalChangeProcessor> local_change_processor;
571  std::vector<FileChange> changes;
572  EXPECT_CALL(local_change_processor, ApplyLocalChange(_, _, _, kURL, _))
573      .WillOnce(MockStatusCallbackAndRecordChange(SYNC_STATUS_OK, &changes));
574  {
575    base::RunLoop run_loop;
576    local_service_->SetLocalChangeProcessor(&local_change_processor);
577    local_service_->ProcessLocalChange(
578        base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
579                   SYNC_STATUS_OK, kURL));
580    run_loop.Run();
581  }
582
583  EXPECT_EQ(1U, changes.size());
584  EXPECT_EQ(change, changes[0]);
585}
586
587// TODO(kinuko): Add tests for multiple file changes and multiple
588// FileSystemContexts.
589
590// Unit test for OriginChangeMap ---------------------------------------------
591
592class OriginChangeMapTest : public testing::Test {
593 protected:
594  OriginChangeMapTest() {}
595  virtual ~OriginChangeMapTest() {}
596
597  bool NextOriginToProcess(GURL* origin) {
598    return map_.NextOriginToProcess(origin);
599  }
600
601  int64 GetTotalChangeCount() const {
602    return map_.GetTotalChangeCount();
603  }
604
605  void SetOriginChangeCount(const GURL& origin, int64 changes) {
606    map_.SetOriginChangeCount(origin, changes);
607  }
608
609  void SetOriginEnabled(const GURL& origin, bool enabled) {
610    map_.SetOriginEnabled(origin, enabled);
611  }
612
613  LocalFileSyncService::OriginChangeMap map_;
614};
615
616TEST_F(OriginChangeMapTest, Basic) {
617  const GURL kOrigin1("chrome-extension://foo");
618  const GURL kOrigin2("chrome-extension://bar");
619  const GURL kOrigin3("chrome-extension://baz");
620
621  ASSERT_EQ(0, GetTotalChangeCount());
622
623  SetOriginChangeCount(kOrigin1, 1);
624  SetOriginChangeCount(kOrigin2, 2);
625
626  ASSERT_EQ(1 + 2, GetTotalChangeCount());
627
628  SetOriginChangeCount(kOrigin3, 4);
629
630  ASSERT_EQ(1 + 2 + 4, GetTotalChangeCount());
631
632  const GURL kOrigins[] = { kOrigin1, kOrigin2, kOrigin3 };
633  std::set<GURL> all_origins;
634  all_origins.insert(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins));
635
636  GURL origin;
637  while (!all_origins.empty()) {
638    ASSERT_TRUE(NextOriginToProcess(&origin));
639    ASSERT_TRUE(ContainsKey(all_origins, origin));
640    all_origins.erase(origin);
641  }
642
643  // Set kOrigin2's change count 0.
644  SetOriginChangeCount(kOrigin2, 0);
645  ASSERT_EQ(1 + 4, GetTotalChangeCount());
646
647  // kOrigin2 won't return this time.
648  all_origins.insert(kOrigin1);
649  all_origins.insert(kOrigin3);
650  while (!all_origins.empty()) {
651    ASSERT_TRUE(NextOriginToProcess(&origin));
652    ASSERT_TRUE(ContainsKey(all_origins, origin));
653    all_origins.erase(origin);
654  }
655
656  // Calling NextOriginToProcess() again will just return
657  // the same set of origins (as far as we don't change the
658  // change count).
659  all_origins.insert(kOrigin1);
660  all_origins.insert(kOrigin3);
661  while (!all_origins.empty()) {
662    ASSERT_TRUE(NextOriginToProcess(&origin));
663    ASSERT_TRUE(ContainsKey(all_origins, origin));
664    all_origins.erase(origin);
665  }
666
667  // Set kOrigin2's change count 8.
668  SetOriginChangeCount(kOrigin2, 8);
669  ASSERT_EQ(1 + 4 + 8, GetTotalChangeCount());
670
671  all_origins.insert(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins));
672  while (!all_origins.empty()) {
673    ASSERT_TRUE(NextOriginToProcess(&origin));
674    ASSERT_TRUE(ContainsKey(all_origins, origin));
675    all_origins.erase(origin);
676  }
677}
678
679TEST_F(OriginChangeMapTest, WithDisabled) {
680  const GURL kOrigin1("chrome-extension://foo");
681  const GURL kOrigin2("chrome-extension://bar");
682  const GURL kOrigin3("chrome-extension://baz");
683  const GURL kOrigins[] = { kOrigin1, kOrigin2, kOrigin3 };
684
685  ASSERT_EQ(0, GetTotalChangeCount());
686
687  SetOriginChangeCount(kOrigin1, 1);
688  SetOriginChangeCount(kOrigin2, 2);
689  SetOriginChangeCount(kOrigin3, 4);
690
691  ASSERT_EQ(1 + 2 + 4, GetTotalChangeCount());
692
693  std::set<GURL> all_origins;
694  all_origins.insert(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins));
695
696  GURL origin;
697  while (!all_origins.empty()) {
698    ASSERT_TRUE(NextOriginToProcess(&origin));
699    ASSERT_TRUE(ContainsKey(all_origins, origin));
700    all_origins.erase(origin);
701  }
702
703  SetOriginEnabled(kOrigin2, false);
704  ASSERT_EQ(1 + 4, GetTotalChangeCount());
705
706  // kOrigin2 won't return this time.
707  all_origins.insert(kOrigin1);
708  all_origins.insert(kOrigin3);
709  while (!all_origins.empty()) {
710    ASSERT_TRUE(NextOriginToProcess(&origin));
711    ASSERT_TRUE(ContainsKey(all_origins, origin));
712    all_origins.erase(origin);
713  }
714
715  // kOrigin1 and kOrigin2 are now disabled.
716  SetOriginEnabled(kOrigin1, false);
717  ASSERT_EQ(4, GetTotalChangeCount());
718
719  ASSERT_TRUE(NextOriginToProcess(&origin));
720  ASSERT_EQ(kOrigin3, origin);
721
722  // Re-enable kOrigin2.
723  SetOriginEnabled(kOrigin2, true);
724  ASSERT_EQ(2 + 4, GetTotalChangeCount());
725
726  // kOrigin1 won't return this time.
727  all_origins.insert(kOrigin2);
728  all_origins.insert(kOrigin3);
729  while (!all_origins.empty()) {
730    ASSERT_TRUE(NextOriginToProcess(&origin));
731    ASSERT_TRUE(ContainsKey(all_origins, origin));
732    all_origins.erase(origin);
733  }
734}
735
736}  // namespace sync_file_system
737