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/drive_backend_v1/drive_file_sync_service.h" 6 7 #include "base/bind.h" 8 #include "base/message_loop/message_loop_proxy.h" 9 #include "base/run_loop.h" 10 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_metadata_store.h" 11 #include "chrome/browser/sync_file_system/drive_backend_v1/fake_api_util.h" 12 #include "chrome/browser/sync_file_system/sync_file_system.pb.h" 13 #include "chrome/browser/sync_file_system/syncable_file_system_util.h" 14 #include "chrome/test/base/testing_profile.h" 15 #include "content/public/test/test_browser_thread_bundle.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 namespace sync_file_system { 19 20 using drive_backend::APIUtilInterface; 21 using drive_backend::FakeAPIUtil; 22 23 namespace { 24 25 const char kSyncRootResourceId[] = "folder:sync_root_resource_id"; 26 27 void DidInitialize(bool* done, SyncStatusCode status, bool created) { 28 EXPECT_EQ(SYNC_STATUS_OK, status); 29 *done = true; 30 } 31 32 void ExpectEqStatus(bool* done, 33 SyncStatusCode expected, 34 SyncStatusCode actual) { 35 EXPECT_FALSE(*done); 36 *done = true; 37 EXPECT_EQ(expected, actual); 38 } 39 40 void ExpectOkStatus(SyncStatusCode status) { 41 EXPECT_EQ(SYNC_STATUS_OK, status); 42 } 43 44 } // namespace 45 46 class DriveFileSyncServiceTest : public testing::Test { 47 public: 48 DriveFileSyncServiceTest() 49 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), 50 fake_api_util_(NULL), 51 metadata_store_(NULL) {} 52 53 virtual void SetUp() OVERRIDE { 54 RegisterSyncableFileSystem(); 55 fake_api_util_ = new FakeAPIUtil; 56 57 ASSERT_TRUE(scoped_base_dir_.CreateUniqueTempDir()); 58 base_dir_ = scoped_base_dir_.path(); 59 metadata_store_ = new DriveMetadataStore( 60 base_dir_, base::MessageLoopProxy::current().get()); 61 bool done = false; 62 metadata_store_->Initialize(base::Bind(&DidInitialize, &done)); 63 base::RunLoop().RunUntilIdle(); 64 metadata_store_->SetSyncRootDirectory(kSyncRootResourceId); 65 EXPECT_TRUE(done); 66 67 sync_service_ = DriveFileSyncService::CreateForTesting( 68 &profile_, 69 base_dir_, 70 scoped_ptr<APIUtilInterface>(fake_api_util_), 71 scoped_ptr<DriveMetadataStore>(metadata_store_)).Pass(); 72 base::RunLoop().RunUntilIdle(); 73 } 74 75 virtual void TearDown() OVERRIDE { 76 metadata_store_ = NULL; 77 fake_api_util_ = NULL; 78 sync_service_.reset(); 79 base::RunLoop().RunUntilIdle(); 80 81 base_dir_ = base::FilePath(); 82 RevokeSyncableFileSystem(); 83 } 84 85 virtual ~DriveFileSyncServiceTest() { 86 } 87 88 protected: 89 FakeAPIUtil* fake_api_util() { return fake_api_util_; } 90 DriveMetadataStore* metadata_store() { return metadata_store_; } 91 DriveFileSyncService* sync_service() { return sync_service_.get(); } 92 std::map<GURL, std::string>* pending_batch_sync_origins() { 93 return &(sync_service()->pending_batch_sync_origins_); 94 } 95 96 // Helper function to add an origin to the given origin sync status. To make 97 // naming easier, each origin, resourceID, etc. will all share the same 98 // prefixes and only be distinguished by the given suffix ID which could be a 99 // number (1, 2, 3, etc.) or a letter (A, B, C, etc.). 100 // e.g. originA, originB, folder:resource_idA, folder:resource_idB, etc. 101 void AddOrigin(std::string status, const char* suffix) { 102 const GURL origin(std::string("chrome-extension://app_") + suffix); 103 const std::string resource_id(std::string("folder:resource_id") + suffix); 104 105 if (status == "Pending") { 106 pending_batch_sync_origins()->insert(std::make_pair(origin, resource_id)); 107 } else if (status == "Enabled") { 108 metadata_store()->AddIncrementalSyncOrigin(origin, resource_id); 109 } else if (status == "Disabled") { 110 metadata_store()->AddIncrementalSyncOrigin(origin, resource_id); 111 metadata_store()->DisableOrigin(origin, base::Bind(&ExpectOkStatus)); 112 } else { 113 NOTREACHED(); 114 } 115 } 116 117 bool VerifyOriginStatusCount(size_t expected_pending, 118 size_t expected_enabled, 119 size_t expected_disabled) { 120 size_t actual_pending = pending_batch_sync_origins()->size(); 121 size_t actual_enabled = metadata_store()->incremental_sync_origins().size(); 122 size_t actual_disabled = metadata_store()->disabled_origins().size(); 123 124 // Prints which counts don't match up if any. 125 EXPECT_EQ(expected_pending, actual_pending); 126 EXPECT_EQ(expected_enabled, actual_enabled); 127 EXPECT_EQ(expected_disabled, actual_disabled); 128 129 // If any count doesn't match, the original line number can be printed by 130 // simply adding ASSERT_TRUE on the call to this function. 131 if (expected_pending == actual_pending && 132 expected_enabled == actual_enabled && 133 expected_disabled == actual_disabled) 134 return true; 135 136 return false; 137 } 138 139 private: 140 base::ScopedTempDir scoped_base_dir_; 141 content::TestBrowserThreadBundle thread_bundle_; 142 143 TestingProfile profile_; 144 base::FilePath base_dir_; 145 146 FakeAPIUtil* fake_api_util_; // Owned by |sync_service_|. 147 DriveMetadataStore* metadata_store_; // Owned by |sync_service_|. 148 149 scoped_ptr<DriveFileSyncService> sync_service_; 150 151 DISALLOW_COPY_AND_ASSIGN(DriveFileSyncServiceTest); 152 }; 153 154 TEST_F(DriveFileSyncServiceTest, UninstallOrigin) { 155 // Add fake app origin directory using fake drive_sync_client. 156 std::string origin_dir_resource_id = "uninstalledappresourceid"; 157 fake_api_util()->PushRemoteChange("parent_id", 158 "parent_title", 159 "uninstall_me_folder", 160 origin_dir_resource_id, 161 "resource_md5", 162 SYNC_FILE_TYPE_FILE, 163 false); 164 165 // Add meta_data entry so GURL->resourse_id mapping is there. 166 const GURL origin_gurl("chrome-extension://uninstallme"); 167 metadata_store()->AddIncrementalSyncOrigin(origin_gurl, 168 origin_dir_resource_id); 169 170 // Delete the origin directory. 171 bool done = false; 172 sync_service()->UninstallOrigin( 173 origin_gurl, 174 RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE, 175 base::Bind(&ExpectEqStatus, &done, SYNC_STATUS_OK)); 176 base::RunLoop().RunUntilIdle(); 177 EXPECT_TRUE(done); 178 179 // Assert the App's origin folder was marked as deleted. 180 EXPECT_TRUE(fake_api_util()->remote_resources().find( 181 origin_dir_resource_id)->second.deleted); 182 } 183 184 TEST_F(DriveFileSyncServiceTest, UninstallUnpackedOrigin) { 185 // Add fake app origin directory using fake drive_sync_client. 186 std::string origin_dir_resource_id = "uninstalledappresourceid"; 187 fake_api_util()->PushRemoteChange("parent_id", 188 "parent_title", 189 "uninstall_me_folder", 190 origin_dir_resource_id, 191 "resource_md5", 192 SYNC_FILE_TYPE_FILE, 193 false); 194 195 // Add meta_data entry so GURL->resourse_id mapping is there. 196 const GURL origin_gurl("chrome-extension://uninstallme"); 197 metadata_store()->AddIncrementalSyncOrigin(origin_gurl, 198 origin_dir_resource_id); 199 200 // Uninstall the origin. 201 bool done = false; 202 sync_service()->UninstallOrigin( 203 origin_gurl, 204 RemoteFileSyncService::UNINSTALL_AND_KEEP_REMOTE, 205 base::Bind(&ExpectEqStatus, &done, SYNC_STATUS_OK)); 206 base::RunLoop().RunUntilIdle(); 207 EXPECT_TRUE(done); 208 209 // Assert the App's origin folder has not been deleted. 210 EXPECT_FALSE(fake_api_util()->remote_resources().find( 211 origin_dir_resource_id)->second.deleted); 212 } 213 214 TEST_F(DriveFileSyncServiceTest, UninstallOriginWithoutOriginDirectory) { 215 // Not add fake app origin directory. 216 std::string origin_dir_resource_id = "uninstalledappresourceid"; 217 218 // Add meta_data entry so GURL->resourse_id mapping is there. 219 const GURL origin_gurl("chrome-extension://uninstallme"); 220 metadata_store()->AddIncrementalSyncOrigin(origin_gurl, 221 origin_dir_resource_id); 222 223 // Delete the origin directory (but not found). 224 bool done = false; 225 sync_service()->UninstallOrigin( 226 origin_gurl, 227 RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE, 228 base::Bind(&ExpectEqStatus, &done, SYNC_STATUS_OK)); 229 base::RunLoop().RunUntilIdle(); 230 EXPECT_TRUE(done); 231 232 // Assert the App's origin folder does not exist. 233 const FakeAPIUtil::RemoteResourceByResourceId& remote_resources = 234 fake_api_util()->remote_resources(); 235 EXPECT_TRUE(remote_resources.find(origin_dir_resource_id) == 236 remote_resources.end()); 237 } 238 239 TEST_F(DriveFileSyncServiceTest, DisableOriginPendingOrigin) { 240 // Disable a pending origin after DriveFileSystemService has already started. 241 const GURL origin("chrome-extension://app"); 242 std::string origin_resource_id = "app_resource_id"; 243 pending_batch_sync_origins()->insert(std::make_pair(origin, 244 origin_resource_id)); 245 ASSERT_TRUE(VerifyOriginStatusCount(1u, 0u, 0u)); 246 247 // Pending origins that are disabled are dropped and do not go to disabled. 248 sync_service()->DisableOrigin(origin, base::Bind(&ExpectOkStatus)); 249 base::RunLoop().RunUntilIdle(); 250 ASSERT_TRUE(VerifyOriginStatusCount(0u, 0u, 0u)); 251 } 252 253 TEST_F(DriveFileSyncServiceTest, 254 DisableOriginIncrementalOrigin) { 255 // Disable a pending origin after DriveFileSystemService has already started. 256 const GURL origin("chrome-extension://app"); 257 std::string origin_resource_id = "app_resource_id"; 258 metadata_store()->AddIncrementalSyncOrigin(origin, origin_resource_id); 259 ASSERT_TRUE(VerifyOriginStatusCount(0u, 1u, 0u)); 260 261 sync_service()->DisableOrigin(origin, base::Bind(&ExpectOkStatus)); 262 base::RunLoop().RunUntilIdle(); 263 ASSERT_TRUE(VerifyOriginStatusCount(0u, 0u, 1u)); 264 } 265 266 TEST_F(DriveFileSyncServiceTest, EnableOrigin) { 267 const GURL origin("chrome-extension://app"); 268 std::string origin_resource_id = "app_resource_id"; 269 metadata_store()->AddIncrementalSyncOrigin(origin, origin_resource_id); 270 metadata_store()->DisableOrigin(origin, base::Bind(&ExpectOkStatus)); 271 ASSERT_TRUE(VerifyOriginStatusCount(0u, 0u, 1u)); 272 273 // Re-enable the previously disabled origin. It initially goes to pending 274 // status and then to enabled (incremental) again when NotifyTasksDone() in 275 // SyncTaskManager invokes MaybeStartFetchChanges() and pending 276 // origins > 0. 277 sync_service()->EnableOrigin(origin, base::Bind(&ExpectOkStatus)); 278 base::RunLoop().RunUntilIdle(); 279 ASSERT_TRUE(VerifyOriginStatusCount(0u, 1u, 0u)); 280 } 281 282 TEST_F(DriveFileSyncServiceTest, GetOriginStatusMap) { 283 RemoteFileSyncService::OriginStatusMap origin_status_map; 284 sync_service()->GetOriginStatusMap(&origin_status_map); 285 ASSERT_EQ(0u, origin_status_map.size()); 286 287 // Add 3 pending, 2 enabled and 1 disabled sync origin. 288 AddOrigin("Pending", "p0"); 289 AddOrigin("Pending", "p1"); 290 AddOrigin("Pending", "p2"); 291 AddOrigin("Enabled", "e0"); 292 AddOrigin("Enabled", "e1"); 293 AddOrigin("Disabled", "d0"); 294 295 sync_service()->GetOriginStatusMap(&origin_status_map); 296 ASSERT_EQ(6u, origin_status_map.size()); 297 EXPECT_EQ("Pending", origin_status_map[GURL("chrome-extension://app_p0")]); 298 EXPECT_EQ("Pending", origin_status_map[GURL("chrome-extension://app_p1")]); 299 EXPECT_EQ("Pending", origin_status_map[GURL("chrome-extension://app_p2")]); 300 EXPECT_EQ("Enabled", origin_status_map[GURL("chrome-extension://app_e0")]); 301 EXPECT_EQ("Enabled", origin_status_map[GURL("chrome-extension://app_e1")]); 302 EXPECT_EQ("Disabled", origin_status_map[GURL("chrome-extension://app_d0")]); 303 } 304 305 } // namespace sync_file_system 306