Home | History | Annotate | Download | only in local
      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 "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
      6 
      7 #include <deque>
      8 #include <set>
      9 
     10 #include "base/basictypes.h"
     11 #include "base/files/scoped_temp_dir.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/stl_util.h"
     15 #include "base/thread_task_runner_handle.h"
     16 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
     17 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
     18 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
     19 #include "chrome/browser/sync_file_system/sync_status_code.h"
     20 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     21 #include "content/public/test/mock_blob_url_request_context.h"
     22 #include "storage/browser/fileapi/file_system_context.h"
     23 #include "storage/browser/quota/quota_manager.h"
     24 #include "testing/gtest/include/gtest/gtest.h"
     25 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
     26 #include "third_party/leveldatabase/src/include/leveldb/env.h"
     27 
     28 using storage::FileSystemContext;
     29 using storage::FileSystemURL;
     30 using storage::FileSystemURLSet;
     31 using content::MockBlobURLRequestContext;
     32 using content::ScopedTextBlob;
     33 
     34 namespace sync_file_system {
     35 
     36 class LocalFileChangeTrackerTest : public testing::Test {
     37  public:
     38   LocalFileChangeTrackerTest()
     39       : in_memory_env_(leveldb::NewMemEnv(leveldb::Env::Default())),
     40         file_system_(GURL("http://example.com"),
     41                      in_memory_env_.get(),
     42                      base::ThreadTaskRunnerHandle::Get().get(),
     43                      base::ThreadTaskRunnerHandle::Get().get()) {}
     44 
     45   virtual void SetUp() OVERRIDE {
     46     file_system_.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
     47 
     48     ASSERT_TRUE(base_dir_.CreateUniqueTempDir());
     49     sync_context_ =
     50         new LocalFileSyncContext(base_dir_.path(),
     51                                  in_memory_env_.get(),
     52                                  base::ThreadTaskRunnerHandle::Get().get(),
     53                                  base::ThreadTaskRunnerHandle::Get().get());
     54     ASSERT_EQ(
     55         sync_file_system::SYNC_STATUS_OK,
     56         file_system_.MaybeInitializeFileSystemContext(sync_context_.get()));
     57   }
     58 
     59   virtual void TearDown() OVERRIDE {
     60     if (sync_context_.get())
     61       sync_context_->ShutdownOnUIThread();
     62     sync_context_ = NULL;
     63 
     64     message_loop_.RunUntilIdle();
     65     file_system_.TearDown();
     66     // Make sure we don't leave the external filesystem.
     67     // (CannedSyncableFileSystem::TearDown does not do this as there may be
     68     // multiple syncable file systems registered for the name)
     69     RevokeSyncableFileSystem();
     70   }
     71 
     72  protected:
     73   FileSystemURL URL(const std::string& path) {
     74     return file_system_.URL(path);
     75   }
     76 
     77   FileSystemContext* file_system_context() {
     78     return file_system_.file_system_context();
     79   }
     80 
     81   LocalFileChangeTracker* change_tracker() {
     82     return file_system_.backend()->change_tracker();
     83   }
     84 
     85   void VerifyAndClearChange(const FileSystemURL& url,
     86                             const FileChange& expected_change) {
     87     SCOPED_TRACE(testing::Message() << url.DebugString() <<
     88                  " expecting:" << expected_change.DebugString());
     89     // Get the changes for URL and verify.
     90     FileChangeList changes;
     91     change_tracker()->GetChangesForURL(url, &changes);
     92     ASSERT_EQ(1U, changes.size());
     93     SCOPED_TRACE(testing::Message() << url.DebugString() <<
     94                  " actual:" << changes.DebugString());
     95     EXPECT_EQ(expected_change, changes.list()[0]);
     96 
     97     // Clear the URL from the change tracker.
     98     change_tracker()->ClearChangesForURL(url);
     99   }
    100 
    101   void DropChangesInTracker() {
    102     change_tracker()->DropAllChanges();
    103   }
    104 
    105   void RestoreChangesFromTrackerDB() {
    106     change_tracker()->CollectLastDirtyChanges(file_system_context());
    107   }
    108 
    109   void GetAllChangedURLs(storage::FileSystemURLSet* urls) {
    110     change_tracker()->GetAllChangedURLs(urls);
    111   }
    112 
    113   base::MessageLoopForIO message_loop_;
    114   base::ScopedTempDir base_dir_;
    115   scoped_ptr<leveldb::Env> in_memory_env_;
    116   CannedSyncableFileSystem file_system_;
    117 
    118  private:
    119   scoped_refptr<LocalFileSyncContext> sync_context_;
    120 
    121   DISALLOW_COPY_AND_ASSIGN(LocalFileChangeTrackerTest);
    122 };
    123 
    124 TEST_F(LocalFileChangeTrackerTest, DemoteAndPromote) {
    125   EXPECT_EQ(base::File::FILE_OK, file_system_.OpenFileSystem());
    126 
    127   const char kPath[] = "foo/bar";
    128   change_tracker()->OnCreateDirectory(URL(kPath));
    129 
    130   FileSystemURLSet urls;
    131   file_system_.GetChangedURLsInTracker(&urls);
    132   ASSERT_EQ(1u, urls.size());
    133   EXPECT_EQ(URL(kPath), *urls.begin());
    134 
    135   change_tracker()->DemoteChangesForURL(URL(kPath));
    136 
    137   file_system_.GetChangedURLsInTracker(&urls);
    138   ASSERT_TRUE(urls.empty());
    139 
    140   change_tracker()->PromoteDemotedChangesForURL(URL(kPath));
    141 
    142   file_system_.GetChangedURLsInTracker(&urls);
    143   ASSERT_EQ(1u, urls.size());
    144   EXPECT_EQ(URL(kPath), *urls.begin());
    145 
    146   change_tracker()->DemoteChangesForURL(URL(kPath));
    147   change_tracker()->OnRemoveDirectory(URL(kPath));
    148 
    149   file_system_.GetChangedURLsInTracker(&urls);
    150   ASSERT_TRUE(urls.empty());
    151 }
    152 
    153 TEST_F(LocalFileChangeTrackerTest, GetChanges) {
    154   EXPECT_EQ(base::File::FILE_OK, file_system_.OpenFileSystem());
    155 
    156   // Test URLs (no parent/child relationships, as we test such cases
    157   // mainly in LocalFileSyncStatusTest).
    158   const char kPath0[] = "test/dir a/dir";
    159   const char kPath1[] = "test/dir b";
    160   const char kPath2[] = "test/foo.txt";
    161   const char kPath3[] = "test/bar";
    162   const char kPath4[] = "temporary/dir a";
    163   const char kPath5[] = "temporary/foo";
    164 
    165   change_tracker()->OnCreateDirectory(URL(kPath0));
    166   change_tracker()->OnRemoveDirectory(URL(kPath0));  // Offset the create.
    167   change_tracker()->OnRemoveDirectory(URL(kPath1));
    168   change_tracker()->OnCreateDirectory(URL(kPath2));
    169   change_tracker()->OnRemoveFile(URL(kPath3));
    170   change_tracker()->OnModifyFile(URL(kPath4));
    171   change_tracker()->OnCreateFile(URL(kPath5));
    172   change_tracker()->OnRemoveFile(URL(kPath5));  // Recorded as 'delete'.
    173 
    174   FileSystemURLSet urls;
    175   file_system_.GetChangedURLsInTracker(&urls);
    176 
    177   EXPECT_EQ(5U, urls.size());
    178   EXPECT_TRUE(ContainsKey(urls, URL(kPath1)));
    179   EXPECT_TRUE(ContainsKey(urls, URL(kPath2)));
    180   EXPECT_TRUE(ContainsKey(urls, URL(kPath3)));
    181   EXPECT_TRUE(ContainsKey(urls, URL(kPath4)));
    182   EXPECT_TRUE(ContainsKey(urls, URL(kPath5)));
    183 
    184   // Changes for kPath0 must have been offset and removed.
    185   EXPECT_FALSE(ContainsKey(urls, URL(kPath0)));
    186 
    187   // GetNextChangedURLs only returns up to max_urls (i.e. 3) urls.
    188   std::deque<FileSystemURL> urls_to_process;
    189   change_tracker()->GetNextChangedURLs(&urls_to_process, 3);
    190   ASSERT_EQ(3U, urls_to_process.size());
    191 
    192   // Let it return all.
    193   urls_to_process.clear();
    194   change_tracker()->GetNextChangedURLs(&urls_to_process, 0);
    195   ASSERT_EQ(5U, urls_to_process.size());
    196 
    197   // The changes must be in the last-modified-time order.
    198   EXPECT_EQ(URL(kPath1), urls_to_process[0]);
    199   EXPECT_EQ(URL(kPath2), urls_to_process[1]);
    200   EXPECT_EQ(URL(kPath3), urls_to_process[2]);
    201   EXPECT_EQ(URL(kPath4), urls_to_process[3]);
    202   EXPECT_EQ(URL(kPath5), urls_to_process[4]);
    203 
    204   // Modify kPath4 again.
    205   change_tracker()->OnModifyFile(URL(kPath4));
    206 
    207   // Now the order must be changed.
    208   urls_to_process.clear();
    209   change_tracker()->GetNextChangedURLs(&urls_to_process, 0);
    210   ASSERT_EQ(5U, urls_to_process.size());
    211   EXPECT_EQ(URL(kPath1), urls_to_process[0]);
    212   EXPECT_EQ(URL(kPath2), urls_to_process[1]);
    213   EXPECT_EQ(URL(kPath3), urls_to_process[2]);
    214   EXPECT_EQ(URL(kPath5), urls_to_process[3]);
    215   EXPECT_EQ(URL(kPath4), urls_to_process[4]);
    216 
    217   // No changes to promote yet, we've demoted no changes.
    218   EXPECT_FALSE(change_tracker()->PromoteDemotedChanges());
    219 
    220   // Demote changes for kPath1 and kPath3.
    221   change_tracker()->DemoteChangesForURL(URL(kPath1));
    222   change_tracker()->DemoteChangesForURL(URL(kPath3));
    223 
    224   // Now we'll get no changes for kPath1 and kPath3 (it's in a separate queue).
    225   urls_to_process.clear();
    226   change_tracker()->GetNextChangedURLs(&urls_to_process, 0);
    227   ASSERT_EQ(3U, urls_to_process.size());
    228   EXPECT_EQ(URL(kPath2), urls_to_process[0]);
    229   EXPECT_EQ(URL(kPath5), urls_to_process[1]);
    230   EXPECT_EQ(URL(kPath4), urls_to_process[2]);
    231 
    232   // Promote changes.
    233   EXPECT_TRUE(change_tracker()->PromoteDemotedChanges());
    234 
    235   // Now we should have kPath1 and kPath3.
    236   urls_to_process.clear();
    237   change_tracker()->GetNextChangedURLs(&urls_to_process, 0);
    238   ASSERT_EQ(5U, urls_to_process.size());
    239   EXPECT_EQ(URL(kPath1), urls_to_process[0]);
    240   EXPECT_EQ(URL(kPath2), urls_to_process[1]);
    241   EXPECT_EQ(URL(kPath3), urls_to_process[2]);
    242   EXPECT_EQ(URL(kPath5), urls_to_process[3]);
    243   EXPECT_EQ(URL(kPath4), urls_to_process[4]);
    244 
    245   // No changes to promote any more.
    246   EXPECT_FALSE(change_tracker()->PromoteDemotedChanges());
    247 
    248 
    249   VerifyAndClearChange(URL(kPath1),
    250                FileChange(FileChange::FILE_CHANGE_DELETE,
    251                           sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
    252   VerifyAndClearChange(URL(kPath2),
    253                FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    254                           sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
    255   VerifyAndClearChange(URL(kPath3),
    256                FileChange(FileChange::FILE_CHANGE_DELETE,
    257                           sync_file_system::SYNC_FILE_TYPE_FILE));
    258   VerifyAndClearChange(URL(kPath4),
    259                FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    260                           sync_file_system::SYNC_FILE_TYPE_FILE));
    261   VerifyAndClearChange(URL(kPath5),
    262                FileChange(FileChange::FILE_CHANGE_DELETE,
    263                           sync_file_system::SYNC_FILE_TYPE_FILE));
    264 }
    265 
    266 TEST_F(LocalFileChangeTrackerTest, RestoreCreateAndModifyChanges) {
    267   EXPECT_EQ(base::File::FILE_OK, file_system_.OpenFileSystem());
    268 
    269   FileSystemURLSet urls;
    270 
    271   const char kPath0[] = "file a";
    272   const char kPath1[] = "dir a";
    273   const char kPath2[] = "dir a/dir";
    274   const char kPath3[] = "dir a/file a";
    275   const char kPath4[] = "dir a/file b";
    276 
    277   file_system_.GetChangedURLsInTracker(&urls);
    278   ASSERT_EQ(0U, urls.size());
    279 
    280   const std::string kData("Lorem ipsum.");
    281   MockBlobURLRequestContext url_request_context(file_system_context());
    282   ScopedTextBlob blob(url_request_context, "blob_id:test", kData);
    283 
    284   // Create files and nested directories.
    285   EXPECT_EQ(base::File::FILE_OK,
    286             file_system_.CreateFile(URL(kPath0)));       // Creates a file.
    287   EXPECT_EQ(base::File::FILE_OK,
    288             file_system_.CreateDirectory(URL(kPath1)));  // Creates a dir.
    289   EXPECT_EQ(base::File::FILE_OK,
    290             file_system_.CreateDirectory(URL(kPath2)));  // Creates another dir.
    291   EXPECT_EQ(base::File::FILE_OK,
    292             file_system_.CreateFile(URL(kPath3)));       // Creates a file.
    293   EXPECT_EQ(base::File::FILE_OK,
    294             file_system_.TruncateFile(URL(kPath3), 1));  // Modifies the file.
    295   EXPECT_EQ(base::File::FILE_OK,
    296             file_system_.CreateFile(URL(kPath4)));    // Creates another file.
    297   EXPECT_EQ(static_cast<int64>(kData.size()),         // Modifies the file.
    298             file_system_.Write(&url_request_context,
    299                                URL(kPath4), blob.GetBlobDataHandle()));
    300 
    301   // Verify the changes.
    302   file_system_.GetChangedURLsInTracker(&urls);
    303   EXPECT_EQ(5U, urls.size());
    304 
    305   // Reset the changes in in-memory tracker.
    306   DropChangesInTracker();
    307 
    308   // Make sure we have no in-memory changes in the tracker.
    309   file_system_.GetChangedURLsInTracker(&urls);
    310   ASSERT_EQ(0U, urls.size());
    311 
    312   RestoreChangesFromTrackerDB();
    313 
    314   // Make sure the changes are restored from the DB.
    315   file_system_.GetChangedURLsInTracker(&urls);
    316   EXPECT_EQ(5U, urls.size());
    317 
    318   VerifyAndClearChange(URL(kPath0),
    319                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    320                                   sync_file_system::SYNC_FILE_TYPE_FILE));
    321   VerifyAndClearChange(URL(kPath1),
    322                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    323                                   sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
    324   VerifyAndClearChange(URL(kPath2),
    325                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    326                                   sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
    327   VerifyAndClearChange(URL(kPath3),
    328                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    329                                   sync_file_system::SYNC_FILE_TYPE_FILE));
    330   VerifyAndClearChange(URL(kPath4),
    331                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    332                                   sync_file_system::SYNC_FILE_TYPE_FILE));
    333 }
    334 
    335 TEST_F(LocalFileChangeTrackerTest, RestoreRemoveChanges) {
    336   EXPECT_EQ(base::File::FILE_OK, file_system_.OpenFileSystem());
    337 
    338   FileSystemURLSet urls;
    339 
    340   const char kPath0[] = "file";
    341   const char kPath1[] = "dir a";
    342   const char kPath2[] = "dir b";
    343   const char kPath3[] = "dir b/file";
    344   const char kPath4[] = "dir b/dir c";
    345   const char kPath5[] = "dir b/dir c/file";
    346 
    347   file_system_.GetChangedURLsInTracker(&urls);
    348   ASSERT_EQ(0U, urls.size());
    349 
    350   // Creates and removes a same file.
    351   EXPECT_EQ(base::File::FILE_OK,
    352             file_system_.CreateFile(URL(kPath0)));
    353   EXPECT_EQ(base::File::FILE_OK,
    354             file_system_.Remove(URL(kPath0), false /* recursive */));
    355 
    356   // Creates and removes a same directory.
    357   EXPECT_EQ(base::File::FILE_OK,
    358             file_system_.CreateDirectory(URL(kPath1)));
    359   EXPECT_EQ(base::File::FILE_OK,
    360             file_system_.Remove(URL(kPath1), false /* recursive */));
    361 
    362   // Creates files and nested directories, then removes the parent directory.
    363   EXPECT_EQ(base::File::FILE_OK,
    364             file_system_.CreateDirectory(URL(kPath2)));
    365   EXPECT_EQ(base::File::FILE_OK,
    366             file_system_.CreateFile(URL(kPath3)));
    367   EXPECT_EQ(base::File::FILE_OK,
    368             file_system_.CreateDirectory(URL(kPath4)));
    369   EXPECT_EQ(base::File::FILE_OK,
    370             file_system_.CreateFile(URL(kPath5)));
    371   EXPECT_EQ(base::File::FILE_OK,
    372             file_system_.Remove(URL(kPath2), true /* recursive */));
    373 
    374   file_system_.GetChangedURLsInTracker(&urls);
    375   EXPECT_EQ(3U, urls.size());
    376 
    377   DropChangesInTracker();
    378 
    379   // Make sure we have no in-memory changes in the tracker.
    380   file_system_.GetChangedURLsInTracker(&urls);
    381   ASSERT_EQ(0U, urls.size());
    382 
    383   RestoreChangesFromTrackerDB();
    384 
    385   // Make sure the changes are restored from the DB.
    386   file_system_.GetChangedURLsInTracker(&urls);
    387   // Since directories to have been reverted (kPath1, kPath2, kPath4) are
    388   // treated as FILE_CHANGE_DELETE, the number of changes should be 6.
    389   EXPECT_EQ(6U, urls.size());
    390 
    391   VerifyAndClearChange(URL(kPath0),
    392                        FileChange(FileChange::FILE_CHANGE_DELETE,
    393                                   sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
    394   VerifyAndClearChange(URL(kPath1),
    395                        FileChange(FileChange::FILE_CHANGE_DELETE,
    396                                   sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
    397   VerifyAndClearChange(URL(kPath2),
    398                        FileChange(FileChange::FILE_CHANGE_DELETE,
    399                                   sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
    400   VerifyAndClearChange(URL(kPath3),
    401                        FileChange(FileChange::FILE_CHANGE_DELETE,
    402                                   sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
    403   VerifyAndClearChange(URL(kPath4),
    404                        FileChange(FileChange::FILE_CHANGE_DELETE,
    405                                   sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
    406   VerifyAndClearChange(URL(kPath5),
    407                        FileChange(FileChange::FILE_CHANGE_DELETE,
    408                                   sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
    409 }
    410 
    411 TEST_F(LocalFileChangeTrackerTest, RestoreCopyChanges) {
    412   EXPECT_EQ(base::File::FILE_OK, file_system_.OpenFileSystem());
    413 
    414   FileSystemURLSet urls;
    415 
    416   const char kPath0[] = "file a";
    417   const char kPath1[] = "dir a";
    418   const char kPath2[] = "dir a/dir";
    419   const char kPath3[] = "dir a/file a";
    420   const char kPath4[] = "dir a/file b";
    421 
    422   const char kPath0Copy[] = "file b";      // To be copied from kPath0
    423   const char kPath1Copy[] = "dir b";       // To be copied from kPath1
    424   const char kPath2Copy[] = "dir b/dir";   // To be copied from kPath2
    425   const char kPath3Copy[] = "dir b/file a";  // To be copied from kPath3
    426   const char kPath4Copy[] = "dir b/file b";  // To be copied from kPath4
    427 
    428   file_system_.GetChangedURLsInTracker(&urls);
    429   ASSERT_EQ(0U, urls.size());
    430 
    431   const std::string kData("Lorem ipsum.");
    432   MockBlobURLRequestContext url_request_context(file_system_context());
    433   ScopedTextBlob blob(url_request_context, "blob_id:test", kData);
    434 
    435   // Create files and nested directories.
    436   EXPECT_EQ(base::File::FILE_OK,
    437             file_system_.CreateFile(URL(kPath0)));       // Creates a file.
    438   EXPECT_EQ(base::File::FILE_OK,
    439             file_system_.CreateDirectory(URL(kPath1)));  // Creates a dir.
    440   EXPECT_EQ(base::File::FILE_OK,
    441             file_system_.CreateDirectory(URL(kPath2)));  // Creates another dir.
    442   EXPECT_EQ(base::File::FILE_OK,
    443             file_system_.CreateFile(URL(kPath3)));       // Creates a file.
    444   EXPECT_EQ(base::File::FILE_OK,
    445             file_system_.TruncateFile(URL(kPath3), 1));  // Modifies the file.
    446   EXPECT_EQ(base::File::FILE_OK,
    447             file_system_.CreateFile(URL(kPath4)));    // Creates another file.
    448   EXPECT_EQ(static_cast<int64>(kData.size()),
    449             file_system_.Write(&url_request_context,   // Modifies the file.
    450                                URL(kPath4), blob.GetBlobDataHandle()));
    451 
    452   // Verify we have 5 changes for preparation.
    453   file_system_.GetChangedURLsInTracker(&urls);
    454   EXPECT_EQ(5U, urls.size());
    455   change_tracker()->ClearChangesForURL(URL(kPath0));
    456   change_tracker()->ClearChangesForURL(URL(kPath1));
    457   change_tracker()->ClearChangesForURL(URL(kPath2));
    458   change_tracker()->ClearChangesForURL(URL(kPath3));
    459   change_tracker()->ClearChangesForURL(URL(kPath4));
    460 
    461   // Make sure we have no changes.
    462   file_system_.GetChangedURLsInTracker(&urls);
    463   EXPECT_TRUE(urls.empty());
    464 
    465   // Copy the file and the parent directory.
    466   EXPECT_EQ(base::File::FILE_OK,
    467             file_system_.Copy(URL(kPath0), URL(kPath0Copy)));  // Copy the file.
    468   EXPECT_EQ(base::File::FILE_OK,
    469             file_system_.Copy(URL(kPath1), URL(kPath1Copy)));  // Copy the dir.
    470 
    471   file_system_.GetChangedURLsInTracker(&urls);
    472   EXPECT_EQ(5U, urls.size());
    473   DropChangesInTracker();
    474 
    475   // Make sure we have no in-memory changes in the tracker.
    476   file_system_.GetChangedURLsInTracker(&urls);
    477   ASSERT_EQ(0U, urls.size());
    478 
    479   RestoreChangesFromTrackerDB();
    480 
    481   // Make sure the changes are restored from the DB.
    482   file_system_.GetChangedURLsInTracker(&urls);
    483   EXPECT_EQ(5U, urls.size());
    484 
    485   VerifyAndClearChange(URL(kPath0Copy),
    486                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    487                                   sync_file_system::SYNC_FILE_TYPE_FILE));
    488   VerifyAndClearChange(URL(kPath1Copy),
    489                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    490                                   sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
    491   VerifyAndClearChange(URL(kPath2Copy),
    492                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    493                                   sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
    494   VerifyAndClearChange(URL(kPath3Copy),
    495                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    496                                   sync_file_system::SYNC_FILE_TYPE_FILE));
    497   VerifyAndClearChange(URL(kPath4Copy),
    498                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    499                                   sync_file_system::SYNC_FILE_TYPE_FILE));
    500 }
    501 
    502 TEST_F(LocalFileChangeTrackerTest, RestoreMoveChanges) {
    503   EXPECT_EQ(base::File::FILE_OK, file_system_.OpenFileSystem());
    504 
    505   FileSystemURLSet urls;
    506 
    507   const char kPath0[] = "file a";
    508   const char kPath1[] = "dir a";
    509   const char kPath2[] = "dir a/file";
    510   const char kPath3[] = "dir a/dir";
    511   const char kPath4[] = "dir a/dir/file";
    512 
    513   const char kPath5[] = "file b";          // To be moved from kPath0.
    514   const char kPath6[] = "dir b";           // To be moved from kPath1.
    515   const char kPath7[] = "dir b/file";      // To be moved from kPath2.
    516   const char kPath8[] = "dir b/dir";       // To be moved from kPath3.
    517   const char kPath9[] = "dir b/dir/file";  // To be moved from kPath4.
    518 
    519   file_system_.GetChangedURLsInTracker(&urls);
    520   ASSERT_EQ(0U, urls.size());
    521 
    522   // Create files and nested directories.
    523   EXPECT_EQ(base::File::FILE_OK,
    524             file_system_.CreateFile(URL(kPath0)));
    525   EXPECT_EQ(base::File::FILE_OK,
    526             file_system_.CreateDirectory(URL(kPath1)));
    527   EXPECT_EQ(base::File::FILE_OK,
    528             file_system_.CreateFile(URL(kPath2)));
    529   EXPECT_EQ(base::File::FILE_OK,
    530             file_system_.CreateDirectory(URL(kPath3)));
    531   EXPECT_EQ(base::File::FILE_OK,
    532             file_system_.CreateFile(URL(kPath4)));
    533 
    534   // Verify we have 5 changes for preparation.
    535   file_system_.GetChangedURLsInTracker(&urls);
    536   EXPECT_EQ(5U, urls.size());
    537   change_tracker()->ClearChangesForURL(URL(kPath0));
    538   change_tracker()->ClearChangesForURL(URL(kPath1));
    539   change_tracker()->ClearChangesForURL(URL(kPath2));
    540   change_tracker()->ClearChangesForURL(URL(kPath3));
    541   change_tracker()->ClearChangesForURL(URL(kPath4));
    542 
    543   // Make sure we have no changes.
    544   file_system_.GetChangedURLsInTracker(&urls);
    545   EXPECT_TRUE(urls.empty());
    546 
    547   // Move the file and the parent directory.
    548   EXPECT_EQ(base::File::FILE_OK,
    549             file_system_.Move(URL(kPath0), URL(kPath5)));
    550   EXPECT_EQ(base::File::FILE_OK,
    551             file_system_.Move(URL(kPath1), URL(kPath6)));
    552 
    553   file_system_.GetChangedURLsInTracker(&urls);
    554   EXPECT_EQ(10U, urls.size());
    555 
    556   DropChangesInTracker();
    557 
    558   // Make sure we have no in-memory changes in the tracker.
    559   file_system_.GetChangedURLsInTracker(&urls);
    560   ASSERT_EQ(0U, urls.size());
    561 
    562   RestoreChangesFromTrackerDB();
    563 
    564   // Make sure the changes are restored from the DB.
    565   file_system_.GetChangedURLsInTracker(&urls);
    566   // Deletion for child files in the deleted directory cannot be restored,
    567   // so we will only have 8 changes.
    568   EXPECT_EQ(10U, urls.size());
    569 
    570   VerifyAndClearChange(URL(kPath0),
    571                        FileChange(FileChange::FILE_CHANGE_DELETE,
    572                                   sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
    573   VerifyAndClearChange(URL(kPath1),
    574                        FileChange(FileChange::FILE_CHANGE_DELETE,
    575                                   sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
    576   VerifyAndClearChange(URL(kPath3),
    577                        FileChange(FileChange::FILE_CHANGE_DELETE,
    578                                   sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
    579   VerifyAndClearChange(URL(kPath5),
    580                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    581                                   sync_file_system::SYNC_FILE_TYPE_FILE));
    582   VerifyAndClearChange(URL(kPath6),
    583                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    584                                   sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
    585   VerifyAndClearChange(URL(kPath7),
    586                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    587                                   sync_file_system::SYNC_FILE_TYPE_FILE));
    588   VerifyAndClearChange(URL(kPath8),
    589                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    590                                   sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
    591   VerifyAndClearChange(URL(kPath9),
    592                        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    593                                   sync_file_system::SYNC_FILE_TYPE_FILE));
    594 }
    595 
    596 TEST_F(LocalFileChangeTrackerTest, NextChangedURLsWithRecursiveCopy) {
    597   EXPECT_EQ(base::File::FILE_OK, file_system_.OpenFileSystem());
    598 
    599   FileSystemURLSet urls;
    600 
    601   const char kPath0[] = "dir a";
    602   const char kPath1[] = "dir a/file";
    603   const char kPath2[] = "dir a/dir";
    604 
    605   const char kPath0Copy[] = "dir b";
    606   const char kPath1Copy[] = "dir b/file";
    607   const char kPath2Copy[] = "dir b/dir";
    608 
    609   // Creates kPath0,1,2 and then copies them all.
    610   EXPECT_EQ(base::File::FILE_OK,
    611             file_system_.CreateDirectory(URL(kPath0)));
    612   EXPECT_EQ(base::File::FILE_OK,
    613             file_system_.CreateFile(URL(kPath1)));
    614   EXPECT_EQ(base::File::FILE_OK,
    615             file_system_.CreateDirectory(URL(kPath2)));
    616   EXPECT_EQ(base::File::FILE_OK,
    617             file_system_.Copy(URL(kPath0), URL(kPath0Copy)));
    618 
    619   std::deque<FileSystemURL> urls_to_process;
    620   change_tracker()->GetNextChangedURLs(&urls_to_process, 0);
    621   ASSERT_EQ(6U, urls_to_process.size());
    622 
    623   // Creation must have occured first.
    624   EXPECT_EQ(URL(kPath0), urls_to_process[0]);
    625   EXPECT_EQ(URL(kPath1), urls_to_process[1]);
    626   EXPECT_EQ(URL(kPath2), urls_to_process[2]);
    627 
    628   // Then recursive copy took place. The exact order cannot be determined
    629   // but the parent directory must have been created first.
    630   EXPECT_EQ(URL(kPath0Copy), urls_to_process[3]);
    631   EXPECT_TRUE(URL(kPath1Copy) == urls_to_process[4] ||
    632               URL(kPath2Copy) == urls_to_process[4]);
    633   EXPECT_TRUE(URL(kPath1Copy) == urls_to_process[5] ||
    634               URL(kPath2Copy) == urls_to_process[5]);
    635 }
    636 
    637 TEST_F(LocalFileChangeTrackerTest, NextChangedURLsWithRecursiveRemove) {
    638   EXPECT_EQ(base::File::FILE_OK, file_system_.OpenFileSystem());
    639 
    640   const char kPath0[] = "dir a";
    641   const char kPath1[] = "dir a/file1";
    642   const char kPath2[] = "dir a/file2";
    643 
    644   // Creates kPath0,1,2 and then removes them all.
    645   EXPECT_EQ(base::File::FILE_OK,
    646             file_system_.CreateDirectory(URL(kPath0)));
    647   EXPECT_EQ(base::File::FILE_OK,
    648             file_system_.CreateFile(URL(kPath1)));
    649   EXPECT_EQ(base::File::FILE_OK,
    650             file_system_.CreateFile(URL(kPath2)));
    651   EXPECT_EQ(base::File::FILE_OK,
    652             file_system_.Remove(URL(kPath0), true /* recursive */));
    653 
    654   FileSystemURLSet urls;
    655   GetAllChangedURLs(&urls);
    656 
    657   // This is actually not really desirable, but since the directory
    658   // creation and deletion have been offset now we only have two
    659   // file deletion changes.
    660   //
    661   // NOTE: This will cause 2 local sync for deleting nonexistent files
    662   // on the remote side.
    663   //
    664   // TODO(kinuko): For micro optimization we could probably restore the ADD
    665   // change type (other than ADD_OR_UPDATE) and offset file ADD+DELETE
    666   // changes too.
    667   ASSERT_EQ(2U, urls.size());
    668 
    669   // The exact order of recursive removal cannot be determined.
    670   EXPECT_TRUE(ContainsKey(urls, URL(kPath1)));
    671   EXPECT_TRUE(ContainsKey(urls, URL(kPath2)));
    672 }
    673 
    674 TEST_F(LocalFileChangeTrackerTest, ResetForFileSystem) {
    675   EXPECT_EQ(base::File::FILE_OK, file_system_.OpenFileSystem());
    676 
    677   const char kPath0[] = "dir a";
    678   const char kPath1[] = "dir a/file";
    679   const char kPath2[] = "dir a/subdir";
    680   const char kPath3[] = "dir b";
    681 
    682   EXPECT_EQ(base::File::FILE_OK,
    683             file_system_.CreateDirectory(URL(kPath0)));
    684   EXPECT_EQ(base::File::FILE_OK,
    685             file_system_.CreateFile(URL(kPath1)));
    686   EXPECT_EQ(base::File::FILE_OK,
    687             file_system_.CreateDirectory(URL(kPath2)));
    688   EXPECT_EQ(base::File::FILE_OK,
    689             file_system_.CreateDirectory(URL(kPath3)));
    690 
    691   FileSystemURLSet urls;
    692   GetAllChangedURLs(&urls);
    693   EXPECT_EQ(4u, urls.size());
    694   EXPECT_TRUE(ContainsKey(urls, URL(kPath0)));
    695   EXPECT_TRUE(ContainsKey(urls, URL(kPath1)));
    696   EXPECT_TRUE(ContainsKey(urls, URL(kPath2)));
    697   EXPECT_TRUE(ContainsKey(urls, URL(kPath3)));
    698 
    699   // Reset all changes for the file system.
    700   change_tracker()->ResetForFileSystem(
    701       file_system_.origin(), file_system_.type());
    702 
    703   GetAllChangedURLs(&urls);
    704   EXPECT_TRUE(urls.empty());
    705 
    706   // Make sure they're gone from the database too.
    707   DropChangesInTracker();
    708   RestoreChangesFromTrackerDB();
    709 
    710   GetAllChangedURLs(&urls);
    711   EXPECT_TRUE(urls.empty());
    712 }
    713 
    714 }  // namespace sync_file_system
    715